/****************************************************************************
 *
 * Copyright 2018 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/*******************************************************************************
 * arch/arm/src/stm32/stm32_otghsdev.c
 *
 *   Copyright (C) 2012-2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *******************************************************************************/

/*******************************************************************************
 * Included Files
 *******************************************************************************/

#include <tinyara/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <tinyara/arch.h>
#include <tinyara/kmalloc.h>
#include <tinyara/usb/usb.h>
#include <tinyara/usb/usbdev.h>
#include <tinyara/usb/usbdev_trace.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "chip.h"
#include "up_arch.h"
#include "up_internal.h"

#include "stm32_otghs.h"

#if defined(CONFIG_USBDEV) && (defined(CONFIG_STM32_OTGHS))

/*******************************************************************************
 * Definitions
 *******************************************************************************/
/* Configuration ***************************************************************/

#ifndef CONFIG_USBDEV_EP0_MAXSIZE
#define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif

#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE
#define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE
#endif

#ifndef CONFIG_USBDEV_MAXPOWER
#define CONFIG_USBDEV_MAXPOWER 100	/* mA */
#endif

/* There is 1.25Kb of FIFO memory.  The default partitions this memory
 * so that there is a TxFIFO allocated for each endpoint and with more
 * memory provided for the common RxFIFO.  A more knowledge-able
 * configuration would not allocate any TxFIFO space to OUT endpoints.
 */

#ifndef CONFIG_USBDEV_RXFIFO_SIZE
#define CONFIG_USBDEV_RXFIFO_SIZE 512
#endif

#ifndef CONFIG_USBDEV_EP0_TXFIFO_SIZE
#define CONFIG_USBDEV_EP0_TXFIFO_SIZE 192
#endif

#ifndef CONFIG_USBDEV_EP1_TXFIFO_SIZE
#define CONFIG_USBDEV_EP1_TXFIFO_SIZE 192
#endif

#ifndef CONFIG_USBDEV_EP2_TXFIFO_SIZE
#define CONFIG_USBDEV_EP2_TXFIFO_SIZE 192
#endif

#ifndef CONFIG_USBDEV_EP3_TXFIFO_SIZE
#define CONFIG_USBDEV_EP3_TXFIFO_SIZE 192
#endif

#if (CONFIG_USBDEV_RXFIFO_SIZE + CONFIG_USBDEV_EP0_TXFIFO_SIZE + \
	 CONFIG_USBDEV_EP2_TXFIFO_SIZE + CONFIG_USBDEV_EP3_TXFIFO_SIZE) > 1280
#error "FIFO allocations exceed FIFO memory size"
#endif

/* The actual FIFO addresses that we use must be aligned to 4-byte boundaries;
 * FIFO sizes must be provided in units of 32-bit words.
 */

#define STM32_RXFIFO_BYTES     ((CONFIG_USBDEV_RXFIFO_SIZE + 3) & ~3)
#define STM32_RXFIFO_WORDS     ((CONFIG_USBDEV_RXFIFO_SIZE + 3) >> 2)

#define STM32_EP0_TXFIFO_BYTES ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) & ~3)
#define STM32_EP0_TXFIFO_WORDS ((CONFIG_USBDEV_EP0_TXFIFO_SIZE + 3) >> 2)

#if STM32_EP0_TXFIFO_WORDS < 16 || STM32_EP0_TXFIFO_WORDS > 256
#error "CONFIG_USBDEV_EP0_TXFIFO_SIZE is out of range"
#endif

#define STM32_EP1_TXFIFO_BYTES ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) & ~3)
#define STM32_EP1_TXFIFO_WORDS ((CONFIG_USBDEV_EP1_TXFIFO_SIZE + 3) >> 2)

#if STM32_EP1_TXFIFO_WORDS < 16
#error "CONFIG_USBDEV_EP1_TXFIFO_SIZE is out of range"
#endif

#define STM32_EP2_TXFIFO_BYTES ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) & ~3)
#define STM32_EP2_TXFIFO_WORDS ((CONFIG_USBDEV_EP2_TXFIFO_SIZE + 3) >> 2)

#if STM32_EP2_TXFIFO_WORDS < 16
#error "CONFIG_USBDEV_EP2_TXFIFO_SIZE is out of range"
#endif

#define STM32_EP3_TXFIFO_BYTES ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) & ~3)
#define STM32_EP3_TXFIFO_WORDS ((CONFIG_USBDEV_EP3_TXFIFO_SIZE + 3) >> 2)

#if STM32_EP3_TXFIFO_WORDS < 16
#error "CONFIG_USBDEV_EP3_TXFIFO_SIZE is out of range"
#endif

/* Debug ***********************************************************************/
/* Trace error codes */

#define STM32_TRACEERR_ALLOCFAIL            0x01
#define STM32_TRACEERR_BADCLEARFEATURE      0x02
#define STM32_TRACEERR_BADDEVGETSTATUS      0x03
#define STM32_TRACEERR_BADEPNO              0x04
#define STM32_TRACEERR_BADEPGETSTATUS       0x05
#define STM32_TRACEERR_BADGETCONFIG         0x06
#define STM32_TRACEERR_BADGETSETDESC        0x07
#define STM32_TRACEERR_BADGETSTATUS         0x08
#define STM32_TRACEERR_BADSETADDRESS        0x09
#define STM32_TRACEERR_BADSETCONFIG         0x0a
#define STM32_TRACEERR_BADSETFEATURE        0x0b
#define STM32_TRACEERR_BADTESTMODE          0x0c
#define STM32_TRACEERR_BINDFAILED           0x0d
#define STM32_TRACEERR_DISPATCHSTALL        0x0e
#define STM32_TRACEERR_DRIVER               0x0f
#define STM32_TRACEERR_DRIVERREGISTERED     0x10
#define STM32_TRACEERR_EP0NOSETUP           0x11
#define STM32_TRACEERR_EP0SETUPSTALLED      0x12
#define STM32_TRACEERR_EPINNULLPACKET       0x13
#define STM32_TRACEERR_EPINUNEXPECTED       0x14
#define STM32_TRACEERR_EPOUTNULLPACKET      0x15
#define STM32_TRACEERR_EPOUTUNEXPECTED      0x16
#define STM32_TRACEERR_INVALIDCTRLREQ       0x17
#define STM32_TRACEERR_INVALIDPARMS         0x18
#define STM32_TRACEERR_IRQREGISTRATION      0x19
#define STM32_TRACEERR_NOEP                 0x1a
#define STM32_TRACEERR_NOTCONFIGURED        0x1b
#define STM32_TRACEERR_EPOUTQEMPTY          0x1c
#define STM32_TRACEERR_EPINREQEMPTY         0x1d
#define STM32_TRACEERR_NOOUTSETUP           0x1e
#define STM32_TRACEERR_POLLTIMEOUT          0x1f

/* Trace interrupt codes */

#define STM32_TRACEINTID_USB                1	/* USB Interrupt entry/exit */
#define STM32_TRACEINTID_INTPENDING         2	/* On each pass through the loop */

#define STM32_TRACEINTID_EPOUT              (10 + 0)	/* First level interrupt decode */
#define STM32_TRACEINTID_EPIN               (10 + 1)
#define STM32_TRACEINTID_MISMATCH           (10 + 2)
#define STM32_TRACEINTID_WAKEUP             (10 + 3)
#define STM32_TRACEINTID_SUSPEND            (10 + 4)
#define STM32_TRACEINTID_SOF                (10 + 5)
#define STM32_TRACEINTID_RXFIFO             (10 + 6)
#define STM32_TRACEINTID_DEVRESET           (10 + 7)
#define STM32_TRACEINTID_ENUMDNE            (10 + 8)
#define STM32_TRACEINTID_IISOIXFR           (10 + 9)
#define STM32_TRACEINTID_IISOOXFR           (10 + 10)
#define STM32_TRACEINTID_SRQ                (10 + 11)
#define STM32_TRACEINTID_OTG                (10 + 12)

#define STM32_TRACEINTID_EPOUT_XFRC         (40 + 0)	/* EPOUT second level decode */
#define STM32_TRACEINTID_EPOUT_EPDISD       (40 + 1)
#define STM32_TRACEINTID_EPOUT_SETUP        (40 + 2)
#define STM32_TRACEINTID_DISPATCH           (40 + 3)

#define STM32_TRACEINTID_GETSTATUS          (50 + 0)	/* EPOUT third level decode */
#define STM32_TRACEINTID_EPGETSTATUS        (50 + 1)
#define STM32_TRACEINTID_DEVGETSTATUS       (50 + 2)
#define STM32_TRACEINTID_IFGETSTATUS        (50 + 3)
#define STM32_TRACEINTID_CLEARFEATURE       (50 + 4)
#define STM32_TRACEINTID_SETFEATURE         (50 + 5)
#define STM32_TRACEINTID_SETADDRESS         (50 + 6)
#define STM32_TRACEINTID_GETSETDESC         (50 + 7)
#define STM32_TRACEINTID_GETCONFIG          (50 + 8)
#define STM32_TRACEINTID_SETCONFIG          (50 + 9)
#define STM32_TRACEINTID_GETSETIF           (50 + 10)
#define STM32_TRACEINTID_SYNCHFRAME         (50 + 11)

#define STM32_TRACEINTID_EPIN_XFRC          (70 + 0)	/* EPIN second level decode */
#define STM32_TRACEINTID_EPIN_TOC           (70 + 1)
#define STM32_TRACEINTID_EPIN_ITTXFE        (70 + 2)
#define STM32_TRACEINTID_EPIN_EPDISD        (70 + 3)
#define STM32_TRACEINTID_EPIN_TXFE          (70 + 4)

#define STM32_TRACEINTID_EPIN_EMPWAIT       (80 + 0)	/* EPIN second level decode */

#define STM32_TRACEINTID_OUTNAK             (90 + 0)	/* RXFLVL second level decode */
#define STM32_TRACEINTID_OUTRECVD           (90 + 1)
#define STM32_TRACEINTID_OUTDONE            (90 + 2)
#define STM32_TRACEINTID_SETUPDONE          (90 + 3)
#define STM32_TRACEINTID_SETUPRECVD         (90 + 4)

/* Endpoints ******************************************************************/

/* Number of endpoints */

#define STM32_NENDPOINTS             (4)	/* ep0-3 x 2 for IN and OUT */

/* Odd physical endpoint numbers are IN; even are OUT */

#define STM32_EPPHYIN2LOG(epphy)     ((uint8_t)(epphy)|USB_DIR_IN)
#define STM32_EPPHYOUT2LOG(epphy)    ((uint8_t)(epphy)|USB_DIR_OUT)

/* Endpoint 0 */

#define EP0                          (0)

/* The set of all enpoints available to the class implementation (1-3) */

#define STM32_EP_AVAILABLE           (0x0e)	/* All available endpoints */

/* Maximum packet sizes for full speed endpoints */

#define STM32_MAXPACKET              (64)	/* Max packet size (1-64) */

/* Delays **********************************************************************/

#define STM32_READY_DELAY            200000
#define STM32_FLUSH_DELAY            200000

/* Request queue operations ****************************************************/

#define stm32_rqempty(ep)            ((ep)->head == NULL)
#define stm32_rqpeek(ep)             ((ep)->head)

/* Standard stuff **************************************************************/

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

/*******************************************************************************
 * Private Types
 *******************************************************************************/

/* Overall device state */

enum stm32_devstate_e {
	DEVSTATE_DEFAULT = 0,		/* Power-up, unconfigured state.  This state simply
								 * means that the device is not yet been given an
								 * address.
								 *   SET:    At initialization, uninitialization,
								 *           reset, and whenever the device address
								 *           is set to zero
								 *   TESTED: Never
								 */
	DEVSTATE_ADDRESSED,			/* Device address has been assigned, not no
								 * configuration has yet been selected.
								 *   SET:    When either a non-zero device address
								 *           is first assigned or when the device
								 *           is unconfigured (with configuration == 0)
								 *   TESTED: never
								 */
	DEVSTATE_CONFIGURED,		/* Address assigned and configured:
								 *   SET:    When the device has been addressed and
								 *           an non-zero configuration has been selected.
								 *   TESTED: In many places to assure that the USB device
								 *           has been properly configured by the host.
								 */
};

/* Endpoint 0 states */

enum stm32_ep0state_e {
	EP0STATE_IDLE = 0,			/* Idle State, leave on receiving a SETUP packet or
								 * epsubmit:
								 *   SET:    In stm32_epin() and stm32_epout() when
								 *           we revert from request processing to
								 *           SETUP processing.
								 *   TESTED: Never
								 */
	EP0STATE_SETUP_OUT,			/* OUT SETUP packet received.  Waiting for the DATA
								 * OUT phase of SETUP Packet to complete before
								 * processing a SETUP command (without a USB request):
								 *   SET:    Set in stm32_rxinterrupt() when SETUP OUT
								 *           packet is received.
								 *   TESTED: In stm32_ep0out_receive()
								 */
	EP0STATE_SETUP_READY,		/* IN SETUP packet received -OR- OUT SETUP packet and
								 * accompanying data have been received.  Processing
								 * of SETUP command will happen soon.
								 *   SET:    (1) stm32_ep0out_receive() when the OUT
								 *           SETUP data phase completes, or (2)
								 *           stm32_rxinterrupt() when an IN SETUP is
								 *           packet received.
								 *   TESTED: Tested in stm32_epout_interrupt() when
								 *           SETUP phase is done to see if the SETUP
								 *           command is ready to be processed.  Also
								 *           tested in stm32_ep0out_setup() just to
								 *           double-check that we have a SETUP request
								 *           and any accompanying data.
								 */
	EP0STATE_SETUP_PROCESS,		/* SETUP Packet is being processed by stm32_ep0out_setup():
								 *   SET:    When SETUP packet received in EP0 OUT
								 *   TESTED: Never
								 */
	EP0STATE_SETUPRESPONSE,		/* Short SETUP response write (without a USB request):
								 *   SET:    When SETUP response is sent by
								 *           stm32_ep0in_setupresponse()
								 *   TESTED: Never
								 */
	EP0STATE_DATA_IN,			/* Waiting for data out stage (with a USB request):
								 *   SET:    In stm32_epin_request() when a write
								 *           request is processed on EP0.
								 *   TESTED: In stm32_epin() to see if we should
								 *           revert to SETUP processing.
								 */
	EP0STATE_DATA_OUT			/* Waiting for data in phase to complete ( with a
								 * USB request)
								 *   SET:    In stm32_epout_request() when a read
								 *           request is processed on EP0.
								 *   TESTED: In stm32_epout() to see if we should
								 *           revert to SETUP processing
								 */
};

/* Parsed control request */

struct stm32_ctrlreq_s {
	uint8_t type;
	uint8_t req;
	uint16_t value;
	uint16_t index;
	uint16_t len;
};

/* A container for a request so that the request may be retained in a list */

struct stm32_req_s {
	struct usbdev_req_s req;	/* Standard USB request */
	struct stm32_req_s *flink;	/* Supports a singly linked list */
};

/* This is the internal representation of an endpoint */

struct stm32_ep_s {
	/* Common endpoint fields.  This must be the first thing defined in the
	 * structure so that it is possible to simply cast from struct usbdev_ep_s
	 * to struct stm32_ep_s.
	 */

	struct usbdev_ep_s ep;		/* Standard endpoint structure */

	/* STM32-specific fields */

	struct stm32_usbdev_s *dev;	/* Reference to private driver data */
	struct stm32_req_s *head;	/* Request list for this endpoint */
	struct stm32_req_s *tail;
	uint8_t epphy;				/* Physical EP address */
	uint8_t eptype: 2;			/* Endpoint type */
	uint8_t active: 1;			/* 1: A request is being processed */
	uint8_t stalled: 1;			/* 1: Endpoint is stalled */
	uint8_t isin: 1;				/* 1: IN Endpoint */
	uint8_t odd: 1;				/* 1: Odd frame */
	uint8_t zlp: 1;				/* 1: Transmit a zero-length-packet (IN EPs only) */
};

/* This structure retains the state of the USB device controller */

struct stm32_usbdev_s {
	/* Common device fields.  This must be the first thing defined in the
	 * structure so that it is possible to simply cast from struct usbdev_s
	 * to struct stm32_usbdev_s.
	 */

	struct usbdev_s usbdev;

	/* The bound device class driver */

	struct usbdevclass_driver_s *driver;

	/* STM32-specific fields */

	uint8_t stalled: 1;			/* 1: Protocol stalled */
	uint8_t selfpowered: 1;		/* 1: Device is self powered */
	uint8_t addressed: 1;		/* 1: Peripheral address has been set */
	uint8_t configured: 1;		/* 1: Class driver has been configured */
	uint8_t wakeup: 1;			/* 1: Device remote wake-up */
	uint8_t dotest: 1;			/* 1: Test mode selected */

	uint8_t devstate: 4;			/* See enum stm32_devstate_e */
	uint8_t ep0state: 4;			/* See enum stm32_ep0state_e */
	uint8_t testmode: 4;			/* Selected test mode */
	uint8_t epavail[2];			/* Bitset of available OUT/IN endpoints */

	/* E0 SETUP data buffering.
	 *
	 * ctrlreq:
	 *   The 8-byte SETUP request is received on the EP0 OUT endpoint and is
	 *   saved.
	 *
	 * ep0data
	 *   For OUT SETUP requests, the SETUP data phase must also complete before
	 *   the SETUP command can be processed.  The pack receipt logic will save
	 *   the accompanying EP0 IN data in ep0data[] before the SETUP command is
	 *   processed.
	 *
	 *   For IN SETUP requests, the DATA phase will occurr AFTER the SETUP
	 *   control request is processed.  In that case, ep0data[] may be used as
	 *   the response buffer.
	 *
	 * ep0datlen
	 *   Lenght of OUT DATA received in ep0data[] (Not used with OUT data)
	 */

	struct usb_ctrlreq_s ctrlreq;
	uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE];
	uint16_t ep0datlen;

	/* The endpoint lists */

	struct stm32_ep_s epin[STM32_NENDPOINTS];
	struct stm32_ep_s epout[STM32_NENDPOINTS];
};

/*******************************************************************************
 * Private Function Prototypes
 *******************************************************************************/

/* Register operations ********************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32_t stm32_getreg(uint32_t addr);
static void stm32_putreg(uint32_t val, uint32_t addr);
#else
#define stm32_getreg(addr)     getreg32(addr)
#define stm32_putreg(val, addr) putreg32(val, addr)
#endif

/* Request queue operations ****************************************************/

static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s *privep);
static bool stm32_req_addlast(FAR struct stm32_ep_s *privep, FAR struct stm32_req_s *req);

/* Low level data transfers and request operations *****************************/
/* Special endpoint 0 data transfer logic */

static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv, FAR uint8_t *data, uint32_t nbytes);
static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv);
static void stm32_ep0in_activate(void);

static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv);

/* IN request and TxFIFO handling */

static void stm32_txfifo_write(FAR struct stm32_ep_s *privep, FAR uint8_t *buf, int nbytes);
static void stm32_epin_transfer(FAR struct stm32_ep_s *privep, FAR uint8_t *buf, int nbytes);
static void stm32_epin_request(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep);

/* OUT request and RxFIFO handling */

static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep, FAR uint8_t *dest, uint16_t len);
static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len);
static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep);
static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep, int bcnt);
static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt);
static void stm32_epout_request(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep);

/* General request handling */

static void stm32_ep_flush(FAR struct stm32_ep_s *privep);
static void stm32_req_complete(FAR struct stm32_ep_s *privep, int16_t result);
static void stm32_req_cancel(FAR struct stm32_ep_s *privep, int16_t status);

/* Interrupt handling **********************************************************/

static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv, uint16_t eplog);
static int stm32_req_dispatch(FAR struct stm32_usbdev_s *priv, FAR const struct usb_ctrlreq_s *ctrl);
static void stm32_usbreset(FAR struct stm32_usbdev_s *priv);

/* Second level OUT endpoint interrupt processing */

static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv, uint16_t index);
static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv, FAR struct stm32_ctrlreq_s *ctrlreq);
static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv);
static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno);
static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv);

/* Second level IN endpoint interrupt processing */

static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv);
static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno);
static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno);
static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv);

/* Other second level interrupt processing */

static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv);
#ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv);
#endif
#ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv);
#endif

/* First level interrupt processing */

static int stm32_usbinterrupt(int irq, FAR void *context);

/* Endpoint operations *********************************************************/
/* Global OUT NAK controls */

static void stm32_enablegonak(FAR struct stm32_ep_s *privep);
static void stm32_disablegonak(FAR struct stm32_ep_s *privep);

/* Endpoint configuration */

static int stm32_epout_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, uint16_t maxpacket);
static int stm32_epin_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, uint16_t maxpacket);
static int stm32_ep_configure(FAR struct usbdev_ep_s *ep, FAR const struct usb_epdesc_s *desc, bool last);
static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv);

/* Endpoint disable */

static void stm32_epout_disable(FAR struct stm32_ep_s *privep);
static void stm32_epin_disable(FAR struct stm32_ep_s *privep);
static int stm32_ep_disable(FAR struct usbdev_ep_s *ep);

/* Endpoint request management */

static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep);
static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *);

/* Endpoint buffer management */

#ifdef CONFIG_USBDEV_DMA
static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes);
static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf);
#endif

/* Endpoint request submission */

static int stm32_ep_submit(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req);

/* Endpoint request cancellation */

static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req);

/* Stall handling */

static int stm32_epout_setstall(FAR struct stm32_ep_s *privep);
static int stm32_epin_setstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_setstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep);
static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume);
static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv);

/* Endpoint allocation */

static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev, uint8_t epno, bool in, uint8_t eptype);
static void stm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);

/* USB device controller operations ********************************************/

static int stm32_getframe(struct usbdev_s *dev);
static int stm32_wakeup(struct usbdev_s *dev);
static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered);
static int stm32_pullup(struct usbdev_s *dev, bool enable);
static void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address);
static int stm32_txfifo_flush(uint32_t txfnum);
static int stm32_rxfifo_flush(void);

/* Initialization **************************************************************/

static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv);
static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv);

/*******************************************************************************
 * Private Data
 *******************************************************************************/
/* Since there is only a single USB interface, all status information can be
 * be simply retained in a single global instance.
 */

static struct stm32_usbdev_s g_otghsdev;

static const struct usbdev_epops_s g_epops = {
	.configure = stm32_ep_configure,
	.disable = stm32_ep_disable,
	.allocreq = stm32_ep_allocreq,
	.freereq = stm32_ep_freereq,
#ifdef CONFIG_USBDEV_DMA
	.allocbuffer = stm32_ep_allocbuffer,
	.freebuffer = stm32_ep_freebuffer,
#endif
	.submit = stm32_ep_submit,
	.cancel = stm32_ep_cancel,
	.stall = stm32_ep_stall,
};

static const struct usbdev_ops_s g_devops = {
	.allocep = stm32_ep_alloc,
	.freeep = stm32_ep_free,
	.getframe = stm32_getframe,
	.wakeup = stm32_wakeup,
	.selfpowered = stm32_selfpowered,
	.pullup = stm32_pullup,
};

/* Device error strings that may be enabled for more desciptive USB trace
 * output.
 */

#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_deverror[] = {
	TRACE_STR(STM32_TRACEERR_ALLOCFAIL),
	TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE),
	TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS),
	TRACE_STR(STM32_TRACEERR_BADEPNO),
	TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS),
	TRACE_STR(STM32_TRACEERR_BADGETCONFIG),
	TRACE_STR(STM32_TRACEERR_BADGETSETDESC),
	TRACE_STR(STM32_TRACEERR_BADGETSTATUS),
	TRACE_STR(STM32_TRACEERR_BADSETADDRESS),
	TRACE_STR(STM32_TRACEERR_BADSETCONFIG),
	TRACE_STR(STM32_TRACEERR_BADSETFEATURE),
	TRACE_STR(STM32_TRACEERR_BADTESTMODE),
	TRACE_STR(STM32_TRACEERR_BINDFAILED),
	TRACE_STR(STM32_TRACEERR_DISPATCHSTALL),
	TRACE_STR(STM32_TRACEERR_DRIVER),
	TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED),
	TRACE_STR(STM32_TRACEERR_EP0NOSETUP),
	TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED),
	TRACE_STR(STM32_TRACEERR_EPINNULLPACKET),
	TRACE_STR(STM32_TRACEERR_EPINUNEXPECTED),
	TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET),
	TRACE_STR(STM32_TRACEERR_EPOUTUNEXPECTED),
	TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ),
	TRACE_STR(STM32_TRACEERR_INVALIDPARMS),
	TRACE_STR(STM32_TRACEERR_IRQREGISTRATION),
	TRACE_STR(STM32_TRACEERR_NOEP),
	TRACE_STR(STM32_TRACEERR_NOTCONFIGURED),
	TRACE_STR(STM32_TRACEERR_EPOUTQEMPTY),
	TRACE_STR(STM32_TRACEERR_EPINREQEMPTY),
	TRACE_STR(STM32_TRACEERR_NOOUTSETUP),
	TRACE_STR(STM32_TRACEERR_POLLTIMEOUT),
	TRACE_STR_END
};
#endif

/* Interrupt event strings that may be enabled for more desciptive USB trace
 * output.
 */

#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_intdecode[] = {
	TRACE_STR(STM32_TRACEINTID_USB),
	TRACE_STR(STM32_TRACEINTID_INTPENDING),
	TRACE_STR(STM32_TRACEINTID_EPOUT),
	TRACE_STR(STM32_TRACEINTID_EPIN),
	TRACE_STR(STM32_TRACEINTID_MISMATCH),
	TRACE_STR(STM32_TRACEINTID_WAKEUP),
	TRACE_STR(STM32_TRACEINTID_SUSPEND),
	TRACE_STR(STM32_TRACEINTID_SOF),
	TRACE_STR(STM32_TRACEINTID_RXFIFO),
	TRACE_STR(STM32_TRACEINTID_DEVRESET),
	TRACE_STR(STM32_TRACEINTID_ENUMDNE),
	TRACE_STR(STM32_TRACEINTID_IISOIXFR),
	TRACE_STR(STM32_TRACEINTID_IISOOXFR),
	TRACE_STR(STM32_TRACEINTID_SRQ),
	TRACE_STR(STM32_TRACEINTID_OTG),
	TRACE_STR(STM32_TRACEINTID_EPOUT_XFRC),
	TRACE_STR(STM32_TRACEINTID_EPOUT_EPDISD),
	TRACE_STR(STM32_TRACEINTID_EPOUT_SETUP),
	TRACE_STR(STM32_TRACEINTID_DISPATCH),
	TRACE_STR(STM32_TRACEINTID_GETSTATUS),
	TRACE_STR(STM32_TRACEINTID_EPGETSTATUS),
	TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS),
	TRACE_STR(STM32_TRACEINTID_IFGETSTATUS),
	TRACE_STR(STM32_TRACEINTID_CLEARFEATURE),
	TRACE_STR(STM32_TRACEINTID_SETFEATURE),
	TRACE_STR(STM32_TRACEINTID_SETADDRESS),
	TRACE_STR(STM32_TRACEINTID_GETSETDESC),
	TRACE_STR(STM32_TRACEINTID_GETCONFIG),
	TRACE_STR(STM32_TRACEINTID_SETCONFIG),
	TRACE_STR(STM32_TRACEINTID_GETSETIF),
	TRACE_STR(STM32_TRACEINTID_SYNCHFRAME),
	TRACE_STR(STM32_TRACEINTID_EPIN_XFRC),
	TRACE_STR(STM32_TRACEINTID_EPIN_TOC),
	TRACE_STR(STM32_TRACEINTID_EPIN_ITTXFE),
	TRACE_STR(STM32_TRACEINTID_EPIN_EPDISD),
	TRACE_STR(STM32_TRACEINTID_EPIN_TXFE),
	TRACE_STR(STM32_TRACEINTID_EPIN_EMPWAIT),
	TRACE_STR(STM32_TRACEINTID_OUTNAK),
	TRACE_STR(STM32_TRACEINTID_OUTRECVD),
	TRACE_STR(STM32_TRACEINTID_OUTDONE),
	TRACE_STR(STM32_TRACEINTID_SETUPDONE),
	TRACE_STR(STM32_TRACEINTID_SETUPRECVD),
	TRACE_STR_END
};
#endif

/*******************************************************************************
 * Public Data
 *******************************************************************************/

/*******************************************************************************
 * Private Functions
 *******************************************************************************/

/*******************************************************************************
 * Name: stm32_getreg
 *
 * Description:
 *   Get the contents of an STM32 register
 *
 *******************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32_t stm32_getreg(uint32_t addr)
{
	static uint32_t prevaddr = 0;
	static uint32_t preval = 0;
	static uint32_t count = 0;

	/* Read the value from the register */

	uint32_t val = getreg32(addr);

	/* Is this the same value that we read from the same registe last time?  Are
	 * we polling the register?  If so, suppress some of the output.
	 */

	if (addr == prevaddr && val == preval) {
		if (count == 0xffffffff || ++count > 3) {
			if (count == 4) {
				lldbg("...\n");
			}
			return val;
		}
	}

	/* No this is a new address or value */

	else {
		/* Did we print "..." for the previous value? */

		if (count > 3) {
			/* Yes.. then show how many times the value repeated */

			lldbg("[repeats %d more times]\n", count - 3);
		}

		/* Save the new address, value, and count */

		prevaddr = addr;
		preval = val;
		count = 1;
	}

	/* Show the register value read */

	lldbg("%08x->%08x\n", addr, val);
	return val;
}
#endif

/*******************************************************************************
 * Name: stm32_putreg
 *
 * Description:
 *   Set the contents of an STM32 register to a value
 *
 *******************************************************************************/

#if defined(CONFIG_STM32_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void stm32_putreg(uint32_t val, uint32_t addr)
{
	/* Show the register value being written */

	lldbg("%08x<-%08x\n", addr, val);

	/* Write the value */

	putreg32(val, addr);
}
#endif

/*******************************************************************************
 * Name: stm32_req_remfirst
 *
 * Description:
 *   Remove a request from the head of an endpoint request queue
 *
 *******************************************************************************/

static FAR struct stm32_req_s *stm32_req_remfirst(FAR struct stm32_ep_s *privep)
{
	FAR struct stm32_req_s *ret = privep->head;

	if (ret) {
		privep->head = ret->flink;
		if (!privep->head) {
			privep->tail = NULL;
		}

		ret->flink = NULL;
	}

	return ret;
}

/*******************************************************************************
 * Name: stm32_req_addlast
 *
 * Description:
 *   Add a request to the end of an endpoint request queue
 *
 *******************************************************************************/

static bool stm32_req_addlast(FAR struct stm32_ep_s *privep, FAR struct stm32_req_s *req)
{
	bool is_empty = !privep->head;

	req->flink = NULL;
	if (is_empty) {
		privep->head = req;
		privep->tail = req;
	} else {
		privep->tail->flink = req;
		privep->tail = req;
	}
	return is_empty;
}

/*******************************************************************************
 * Name: stm32_ep0in_setupresponse
 *
 * Description:
 *   Schedule a short transfer on Endpoint 0 (IN or OUT)
 *
 *******************************************************************************/

static void stm32_ep0in_setupresponse(FAR struct stm32_usbdev_s *priv, FAR uint8_t *buf, uint32_t nbytes)
{
	stm32_epin_transfer(&priv->epin[EP0], buf, nbytes);
	priv->ep0state = EP0STATE_SETUPRESPONSE;
	stm32_ep0out_ctrlsetup(priv);
}

/****************************************************************************
 * Name: stm32_ep0in_transmitzlp
 *
 * Description:
 *   Send a zero length packet (ZLP) on endpoint 0 IN
 *
 ****************************************************************************/

static inline void stm32_ep0in_transmitzlp(FAR struct stm32_usbdev_s *priv)
{
	stm32_ep0in_setupresponse(priv, NULL, 0);
}

/*******************************************************************************
 * Name: stm32_ep0in_activate
 *
 * Description:
 *   Activate the endpoint 0 IN endpoint.
 *
 *******************************************************************************/

static void stm32_ep0in_activate(void)
{
	uint32_t regval;

	/* Set the max packet size  of the IN EP. */

	regval = stm32_getreg(STM32_OTGHS_DIEPCTL0);
	regval &= ~OTGHS_DIEPCTL0_MPSIZ_MASK;

#if CONFIG_USBDEV_EP0_MAXSIZE == 8
	regval |= OTGHS_DIEPCTL0_MPSIZ_8;
#elif CONFIG_USBDEV_EP0_MAXSIZE == 16
	regval |= OTGHS_DIEPCTL0_MPSIZ_16;
#elif CONFIG_USBDEV_EP0_MAXSIZE == 32
	regval |= OTGHS_DIEPCTL0_MPSIZ_32;
#elif CONFIG_USBDEV_EP0_MAXSIZE == 64
	regval |= OTGHS_DIEPCTL0_MPSIZ_64;
#else
#error "Unsupported value of CONFIG_USBDEV_EP0_MAXSIZE"
#endif

	stm32_putreg(regval, STM32_OTGHS_DIEPCTL0);

	/* Clear global IN NAK */

	regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval |= OTGHS_DCTL_CGINAK;
	stm32_putreg(regval, STM32_OTGHS_DCTL);
}

/*******************************************************************************
 * Name: stm32_ep0out_ctrlsetup
 *
 * Description:
 *   Setup to receive a SETUP packet.
 *
 *******************************************************************************/

static void stm32_ep0out_ctrlsetup(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval;

	/* Setup the hardware to perform the SETUP transfer */

	regval = (USB_SIZEOF_CTRLREQ * 3 << OTGHS_DOEPTSIZ0_XFRSIZ_SHIFT) | (OTGHS_DOEPTSIZ0_PKTCNT) | (3 << OTGHS_DOEPTSIZ0_STUPCNT_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DOEPTSIZ0);

	/* Then clear NAKing and enable the transfer */

	regval = stm32_getreg(STM32_OTGHS_DOEPCTL0);
	regval |= (OTGHS_DOEPCTL0_CNAK | OTGHS_DOEPCTL0_EPENA);
	stm32_putreg(regval, STM32_OTGHS_DOEPCTL0);
}

/****************************************************************************
 * Name: stm32_txfifo_write
 *
 * Description:
 *   Send data to the endpoint's TxFIFO.
 *
 ****************************************************************************/

static void stm32_txfifo_write(FAR struct stm32_ep_s *privep, FAR uint8_t *buf, int nbytes)
{
	uint32_t regaddr;
	uint32_t regval;
	int nwords;
	int i;

	/* Convert the number of bytes to words */

	nwords = (nbytes + 3) >> 2;

	/* Get the TxFIFO for this endpoint (same as the endpoint number) */

	regaddr = STM32_OTGHS_DFIFO_DEP(privep->epphy);

	/* Then transfer each word to the TxFIFO */

	for (i = 0; i < nwords; i++) {
		/* Read four bytes from the source buffer (to avoid unaligned accesses)
		 * and pack these into one 32-bit word (little endian).
		 */

		regval = (uint32_t)*buf++;
		regval |= ((uint32_t)*buf++) << 8;
		regval |= ((uint32_t)*buf++) << 16;
		regval |= ((uint32_t)*buf++) << 24;

		/* Then write the packet data to the TxFIFO */

		stm32_putreg(regval, regaddr);
	}
}

/****************************************************************************
 * Name: stm32_epin_transfer
 *
 * Description:
 *   Start the Tx data transfer
 *
 ****************************************************************************/

static void stm32_epin_transfer(FAR struct stm32_ep_s *privep, FAR uint8_t *buf, int nbytes)
{
	uint32_t pktcnt;
	uint32_t regval;

	/* Read the DIEPSIZx register */

	regval = stm32_getreg(STM32_OTGHS_DIEPTSIZ(privep->epphy));

	/* Clear the XFRSIZ, PKTCNT, and MCNT field of the DIEPSIZx register */

	regval &= ~(OTGHS_DIEPTSIZ_XFRSIZ_MASK | OTGHS_DIEPTSIZ_PKTCNT_MASK | OTGHS_DIEPTSIZ_MCNT_MASK);

	/* Are we sending a zero length packet (ZLP) */

	if (nbytes == 0) {
		/* Yes.. leave the transfer size at zero and set the packet count to 1 */

		pktcnt = 1;
	} else {
		/* No.. Program the transfer size and packet count .  First calculate:
		 *
		 * xfrsize = The total number of bytes to be sent.
		 * pktcnt  = the number of packets (of maxpacket bytes) required to
		 *   perform the transfer.
		 */

		pktcnt = ((uint32_t)nbytes + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket;
	}

	/* Set the XFRSIZ and PKTCNT */

	regval |= (pktcnt << OTGHS_DIEPTSIZ_PKTCNT_SHIFT);
	regval |= ((uint32_t)nbytes << OTGHS_DIEPTSIZ_XFRSIZ_SHIFT);

	/* If this is an isconchronous endpoint, then set the multi-count field to
	 * the PKTCNT as well.
	 */

	if (privep->eptype == USB_EP_ATTR_XFER_ISOC) {
		regval |= (pktcnt << OTGHS_DIEPTSIZ_MCNT_SHIFT);
	}

	/* Save DIEPSIZx register value */

	stm32_putreg(regval, STM32_OTGHS_DIEPTSIZ(privep->epphy));

	/* Read the DIEPCTLx register */

	regval = stm32_getreg(STM32_OTGHS_DIEPCTL(privep->epphy));

	/* If this is an isochronous endpoint, then set the even/odd frame bit
	 * the DIEPCTLx register.
	 */

	if (privep->eptype == USB_EP_ATTR_XFER_ISOC) {
		/* Check bit 0 of the frame number of the received SOF and set the
		 * even/odd frame to match.
		 */

		uint32_t status = stm32_getreg(STM32_OTGHS_DSTS);
		if ((status & OTGHS_DSTS_SOFFN0) == OTGHS_DSTS_SOFFN_EVEN) {
			regval |= OTGHS_DIEPCTL_SEVNFRM;
		} else {
			regval |= OTGHS_DIEPCTL_SODDFRM;
		}
	}

	/* EP enable, IN data in FIFO */

	regval &= ~OTGHS_DIEPCTL_EPDIS;
	regval |= (OTGHS_DIEPCTL_CNAK | OTGHS_DIEPCTL_EPENA);
	stm32_putreg(regval, STM32_OTGHS_DIEPCTL(privep->epphy));

	/* Transfer the data to the TxFIFO.  At this point, the caller has already
	 * assured that there is sufficient space in the TxFIFO to hold the transfer
	 * we can just blindly continue.
	 */

	stm32_txfifo_write(privep, buf, nbytes);
}

/****************************************************************************
 * Name: stm32_epin_request
 *
 * Description:
 *   Begin or continue write request processing.
 *
 ****************************************************************************/

static void stm32_epin_request(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep)
{
	struct stm32_req_s *privreq;
	uint32_t regaddr;
	uint32_t regval;
	uint8_t *buf;
	int nbytes;
	int nwords;
	int bytesleft;

	/* We get here in one of four possible ways.  From three interrupting
	 * events:
	 *
	 * 1. From stm32_epin as part of the transfer complete interrupt processing
	 *    This interrupt indicates that the last transfer has completed.
	 * 2. As part of the ITTXFE interrupt processing.  That interrupt indicates
	 *    that an IN token was received when the associated TxFIFO was empty.
	 * 3. From stm32_epin_txfifoempty as part of the TXFE interrupt processing.
	 *    The TXFE interrupt is only enabled when the TxFIFO is full and the
	 *    software must wait for space to become available in the TxFIFO.
	 *
	 * And this function may be called immediately when the write request is
	 * queue to start up the next transaction.
	 *
	 * 4. From stm32_ep_submit when a new write request is received WHILE the
	 *    endpoint is not active (privep->active == false).
	 */

	/* Check the request from the head of the endpoint request queue */

	privreq = stm32_rqpeek(privep);
	if (!privreq) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINREQEMPTY), privep->epphy);

		/* There is no TX transfer in progress and no new pending TX
		 * requests to send.  To stop transmitting any data on a particular
		 * IN endpoint, the application must set the IN NAK bit. To set this
		 * bit, the following field must be programmed.
		 */

		regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
		regval = stm32_getreg(regaddr);
		regval |= OTGHS_DIEPCTL_SNAK;
		stm32_putreg(regval, regaddr);

		/* The endpoint is no longer active */

		privep->active = false;
		return;
	}

	ullvdbg("EP%d req=%p: len=%d xfrd=%d zlp=%d\n", privep->epphy, privreq, privreq->req.len, privreq->req.xfrd, privep->zlp);

	/* Check for a special case:  If we are just starting a request (xfrd==0) and
	 * the class driver is trying to send a zero-length packet (len==0).  Then set
	 * the ZLP flag so that the packet will be sent.
	 */

	if (privreq->req.len == 0) {
		/* The ZLP flag is set TRUE whenever we want to force the driver to
		 * send a zero-length-packet on the next pass through the loop (below).
		 * The flag is cleared whenever a packet is sent in the loop below.
		 */

		privep->zlp = true;
	}

	/* Add one more packet to the TxFIFO.  We will wait for the transfer
	 * complete event before we add the next packet (or part of a packet
	 * to the TxFIFO).
	 *
	 * The documentation says that we can can multiple packets to the TxFIFO,
	 * but it seems that we need to get the transfer complete event before
	 * we can add the next (or maybe I have got something wrong?)
	 */

#if 0
	while (privreq->req.xfrd < privreq->req.len || privep->zlp)
#else
	if (privreq->req.xfrd < privreq->req.len || privep->zlp)
#endif
	{
		/* Get the number of bytes left to be sent in the request */

		bytesleft = privreq->req.len - privreq->req.xfrd;
		nbytes = bytesleft;

		/* Assume no zero-length-packet on the next pass through this loop */

		privep->zlp = false;

		/* Limit the size of the transfer to one full packet and handle
		 * zero-length packets (ZLPs).
		 */

		if (nbytes > 0) {
			/* Either send the maxpacketsize or all of the remaining data in
			 * the request.
			 */

			if (nbytes >= privep->ep.maxpacket) {
				nbytes = privep->ep.maxpacket;

				/* Handle the case where this packet is exactly the
				 * maxpacketsize.  Do we need to send a zero-length packet
				 * in this case?
				 */

				if (bytesleft == privep->ep.maxpacket && (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) {
					/* The ZLP flag is set TRUE whenever we want to force
					 * the driver to send a zero-length-packet on the next
					 * pass through this loop. The flag is cleared (above)
					 * whenever we are committed to sending any packet and
					 * set here when we want to force one more pass through
					 * the loop.
					 */

					privep->zlp = true;
				}
			}
		}

		/* Get the transfer size in 32-bit words */

		nwords = (nbytes + 3) >> 2;

		/* Get the number of 32-bit words available in the TxFIFO. The
		 * DXTHSTS indicates the amount of free space available in the
		 * endpoint TxFIFO. Values are in terms of 32-bit words:
		 *
		 *   0: Endpoint TxFIFO is full
		 *   1: 1 word available
		 *   2: 2 words available
		 *   n: n words available
		 */

		regaddr = STM32_OTGHS_DTXFSTS(privep->epphy);

		/* Check for space in the TxFIFO.  If space in the TxFIFO is not
		 * available, then set up an interrupt to resume the transfer when
		 * the TxFIFO is empty.
		 */

		regval = stm32_getreg(regaddr);
		if ((int)(regval & OTGHS_DTXFSTS_MASK) < nwords) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EMPWAIT), (uint16_t)regval);

			/* There is insufficient space in the TxFIFO.  Wait for a TxFIFO
			 * empty interrupt and try again.
			 */

			uint32_t empmsk = stm32_getreg(STM32_OTGHS_DIEPEMPMSK);
			empmsk |= OTGHS_DIEPEMPMSK(privep->epphy);
			stm32_putreg(empmsk, STM32_OTGHS_DIEPEMPMSK);

			/* Terminate the transfer.  We will try again when the TxFIFO empty
			 * interrupt is received.
			 */

			return;
		}

		/* Transfer data to the TxFIFO */

		buf = privreq->req.buf + privreq->req.xfrd;
		stm32_epin_transfer(privep, buf, nbytes);

		/* If it was not before, the OUT endpoint is now actively transferring
		 * data.
		 */

		privep->active = true;

		/* EP0 is a special case */

		if (privep->epphy == EP0) {
			priv->ep0state = EP0STATE_DATA_IN;
		}

		/* Update for the next time through the loop */

		privreq->req.xfrd += nbytes;
	}

	/* Note that the ZLP, if any, must be sent as a separate transfer.  The need
	 * for a ZLP is indicated by privep->zlp.  If all of the bytes were sent
	 * (including any final null packet) then we are finished with the transfer
	 */

	if (privreq->req.xfrd >= privreq->req.len && !privep->zlp) {
		usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);

		/* We are finished with the request (although the transfer has not
		 * yet completed).
		 */

		stm32_req_complete(privep, OK);
	}
}

/*******************************************************************************
 * Name: stm32_rxfifo_read
 *
 * Description:
 *   Read packet from the RxFIFO into a read request.
 *
 *******************************************************************************/

static void stm32_rxfifo_read(FAR struct stm32_ep_s *privep, FAR uint8_t *dest, uint16_t len)
{
	uint32_t regaddr;
	int i;

	/* Get the address of the RxFIFO.  Note:  there is only one RxFIFO so
	 * we might as well use the addess associated with EP0.
	 */

	regaddr = STM32_OTGHS_DFIFO_DEP(EP0);

	/* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */

	for (i = 0; i < len; i += 4) {
		union {
			uint32_t w;
			uint8_t b[4];
		} data;

		/* Read 1 x 32-bits of EP0 packet data */

		data.w = stm32_getreg(regaddr);

		/* Write 4 x 8-bits of EP0 packet data */

		*dest++ = data.b[0];
		*dest++ = data.b[1];
		*dest++ = data.b[2];
		*dest++ = data.b[3];
	}
}

/*******************************************************************************
 * Name: stm32_rxfifo_discard
 *
 * Description:
 *   Discard packet data from the RxFIFO.
 *
 *******************************************************************************/

static void stm32_rxfifo_discard(FAR struct stm32_ep_s *privep, int len)
{
	if (len > 0) {
		uint32_t regaddr;
		int i;

		/* Get the address of the RxFIFO  Note:  there is only one RxFIFO so
		 * we might as well use the addess associated with EP0.
		 */

		regaddr = STM32_OTGHS_DFIFO_DEP(EP0);

		/* Read 32-bits at time */

		for (i = 0; i < len; i += 4) {
			volatile uint32_t data = stm32_getreg(regaddr);
			(void)data;
		}
	}
}

/*******************************************************************************
 * Name: stm32_epout_complete
 *
 * Description:
 *   This function is called when an OUT transfer complete interrupt is
 *   received.  It completes the read request at the head of the endpoint's
 *   request queue.
 *
 *******************************************************************************/

static void stm32_epout_complete(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep)
{
	struct stm32_req_s *privreq;

	/* Since a transfer just completed, there must be a read request at the head of
	 * the endpoint request queue.
	 */

	privreq = stm32_rqpeek(privep);
	DEBUGASSERT(privreq);

	if (!privreq) {
		/* An OUT transfer completed, but no packet to receive the data.  This
		 * should not happen.
		 */

		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy);
		privep->active = false;
		return;
	}

	ullvdbg("EP%d: len=%d xfrd=%d\n", privep->epphy, privreq->req.len, privreq->req.xfrd);

	/* Return the completed read request to the class driver and mark the state
	 * IDLE.
	 */

	usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
	stm32_req_complete(privep, OK);
	privep->active = false;

	/* Now set up the next read request (if any) */

	stm32_epout_request(priv, privep);
}

/*******************************************************************************
 * Name: stm32_ep0out_receive
 *
 * Description:
 *   This function is called from the RXFLVL interrupt handler when new incoming
 *   data is available in the endpoint's RxFIFO.  This function will simply
 *   copy the incoming data into pending request's data buffer.
 *
 *******************************************************************************/

static inline void stm32_ep0out_receive(FAR struct stm32_ep_s *privep, int bcnt)
{
	FAR struct stm32_usbdev_s *priv;

	/* Sanity Checking */

	DEBUGASSERT(privep && privep->ep.priv);
	priv = (FAR struct stm32_usbdev_s *)privep->ep.priv;

	ullvdbg("EP0: bcnt=%d\n", bcnt);
	usbtrace(TRACE_READ(EP0), bcnt);

	/* Verify that an OUT SETUP request as received before this data was
	 * received in the RxFIFO.
	 */

	if (priv->ep0state == EP0STATE_SETUP_OUT) {
		/* Read the data into our special buffer for SETUP data */

		int readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, bcnt);
		stm32_rxfifo_read(privep, priv->ep0data, readlen);

		/* Do we have to discard any excess bytes? */

		stm32_rxfifo_discard(privep, bcnt - readlen);

		/* Now we can process the setup command */

		privep->active = false;
		priv->ep0state = EP0STATE_SETUP_READY;
		priv->ep0datlen = readlen;

		stm32_ep0out_setup(priv);
	} else {
		/* This is an error.  We don't have any idea what to do with the EP0
		 * data in this case.  Just read and discard it so that the RxFIFO
		 * does not become constipated.
		 */

		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOOUTSETUP), priv->ep0state);
		stm32_rxfifo_discard(privep, bcnt);
		privep->active = false;
	}
}

/*******************************************************************************
 * Name: stm32_epout_receive
 *
 * Description:
 *   This function is called from the RXFLVL interrupt handler when new incoming
 *   data is available in the endpoint's RxFIFO.  This function will simply
 *   copy the incoming data into pending request's data buffer.
 *
 *******************************************************************************/

static inline void stm32_epout_receive(FAR struct stm32_ep_s *privep, int bcnt)
{
	struct stm32_req_s *privreq;
	uint8_t *dest;
	int buflen;
	int readlen;

	/* Get a reference to the request at the head of the endpoint's request
	 * queue.
	 */

	privreq = stm32_rqpeek(privep);
	if (!privreq) {
		/* Incoming data is available in the RxFIFO, but there is no read setup
		 * to receive the receive the data.  This should not happen for data
		 * endpoints; those endpoints should have been NAKing any OUT data tokens.
		 *
		 * We should get here normally on OUT data phase following an OUT
		 * SETUP command.  EP0 data will still receive data in this case and it
		 * should not be NAKing.
		 */

		if (privep->epphy == 0) {
			stm32_ep0out_receive(privep, bcnt);
		} else {
			/* Otherwise, the data is lost. This really should not happen if
			 * NAKing is working as expected.
			 */

			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy);

			/* Discard the data in the RxFIFO */

			stm32_rxfifo_discard(privep, bcnt);
		}

		privep->active = false;
		return;
	}

	ullvdbg("EP%d: len=%d xfrd=%d\n", privep->epphy, privreq->req.len, privreq->req.xfrd);
	usbtrace(TRACE_READ(privep->epphy), bcnt);

	/* Get the number of bytes to transfer from the RxFIFO */

	buflen = privreq->req.len - privreq->req.xfrd;
	DEBUGASSERT(buflen > 0 && buflen >= bcnt);
	readlen = MIN(buflen, bcnt);

	/* Get the destination of the data transfer */

	dest = privreq->req.buf + privreq->req.xfrd;

	/* Transfer the data from the RxFIFO to the request's data buffer */

	stm32_rxfifo_read(privep, dest, readlen);

	/* If there were more bytes in the RxFIFO than could be held in the read
	 * request, then we will have to discard those.
	 */

	stm32_rxfifo_discard(privep, bcnt - readlen);

	/* Update the number of bytes transferred */

	privreq->req.xfrd += readlen;
}

/*******************************************************************************
 * Name: stm32_epout_request
 *
 * Description:
 *   This function is called when either (1) new read request is received, or
 *   (2) a pending receive request completes.  If there is no read in pending,
 *   then this function will initiate the next OUT (read) operation.
 *
 *******************************************************************************/

static void stm32_epout_request(FAR struct stm32_usbdev_s *priv, FAR struct stm32_ep_s *privep)
{
	struct stm32_req_s *privreq;
	uint32_t regaddr;
	uint32_t regval;
	uint32_t xfrsize;
	uint32_t pktcnt;

	/* Make sure that there is not already a pending request request.  If there is,
	 * just return, leaving the newly received request in the request queue.
	 */

	if (!privep->active) {
		/* Loop until a valid request is found (or the request queue is empty).
		 * The loop is only need to look at the request queue again is an invalid
		 * read request is encountered.
		 */

		for (;;) {
			/* Get a reference to the request at the head of the endpoint's request queue */

			privreq = stm32_rqpeek(privep);
			if (!privreq) {
				usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTQEMPTY), privep->epphy);

				/* There are no read requests to be setup.  Configure the hardware to
				 * NAK any incoming packets.  (This should already be the case.  I
				 * think that the hardware will automatically NAK after a transfer is
				 * completed until SNAK is cleared).
				 */

				regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
				regval = stm32_getreg(regaddr);
				regval |= OTGHS_DOEPCTL_SNAK;
				stm32_putreg(regval, regaddr);

				/* This endpoint is no longer actively transferring */

				privep->active = false;
				return;
			}

			ullvdbg("EP%d: len=%d\n", privep->epphy, privreq->req.len);

			/* Ignore any attempt to receive a zero length packet (this really
			 * should not happen.
			 */

			if (privreq->req.len <= 0) {
				usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0);
				stm32_req_complete(privep, OK);
			}

			/* Otherwise, we have a usable read request... break out of the loop */

			else {
				break;
			}
		}

		/* Setup the pending read into the request buffer.  First calculate:
		 *
		 * pktcnt  = the number of packets (of maxpacket bytes) required to
		 *   perform the transfer.
		 * xfrsize = The total number of bytes required (in units of
		 *   maxpacket bytes).
		 */

		pktcnt = (privreq->req.len + (privep->ep.maxpacket - 1)) / privep->ep.maxpacket;
		xfrsize = pktcnt * privep->ep.maxpacket;

		/* Then setup the hardware to perform this transfer */

		regaddr = STM32_OTGHS_DOEPTSIZ(privep->epphy);
		regval = stm32_getreg(regaddr);
		regval &= ~(OTGHS_DOEPTSIZ_XFRSIZ_MASK | OTGHS_DOEPTSIZ_PKTCNT_MASK);
		regval |= (xfrsize << OTGHS_DOEPTSIZ_XFRSIZ_SHIFT);
		regval |= (pktcnt << OTGHS_DOEPTSIZ_PKTCNT_SHIFT);
		stm32_putreg(regval, regaddr);

		/* Then enable the transfer */

		regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
		regval = stm32_getreg(regaddr);

		/* When an isochronous transfer is enabled the Even/Odd frame bit must
		 * also be set appropriately.
		 */

#ifdef CONFIG_USBDEV_ISOCHRONOUS
		if (privep->eptype == USB_EP_ATTR_XFER_ISOC) {
			if (privep->odd) {
				regval |= OTGHS_DOEPCTL_SODDFRM;
			} else {
				regval |= OTGHS_DOEPCTL_SEVNFRM;
			}
		}
#endif

		/* Clearing NAKing and enable the transfer. */

		regval |= (OTGHS_DOEPCTL_CNAK | OTGHS_DOEPCTL_EPENA);
		stm32_putreg(regval, regaddr);

		/* A transfer is now active on this endpoint */

		privep->active = true;

		/* EP0 is a special case.  We need to know when to switch back to
		 * normal SETUP processing.
		 */

		if (privep->epphy == EP0) {
			priv->ep0state = EP0STATE_DATA_OUT;
		}
	}
}

/*******************************************************************************
 * Name: stm32_ep_flush
 *
 * Description:
 *   Flush any primed descriptors from this ep
 *
 *******************************************************************************/

static void stm32_ep_flush(struct stm32_ep_s *privep)
{
	if (privep->isin) {
		stm32_txfifo_flush(OTGHS_GRSTCTL_TXFNUM_D(privep->epphy));
	} else {
		stm32_rxfifo_flush();
	}
}

/*******************************************************************************
 * Name: stm32_req_complete
 *
 * Description:
 *   Handle termination of the request at the head of the endpoint request queue.
 *
 *******************************************************************************/

static void stm32_req_complete(struct stm32_ep_s *privep, int16_t result)
{
	FAR struct stm32_req_s *privreq;

	/* Remove the request at the head of the request list */

	privreq = stm32_req_remfirst(privep);
	DEBUGASSERT(privreq != NULL);

	/* If endpoint 0, temporarily reflect the state of protocol stalled
	 * in the callback.
	 */

	bool stalled = privep->stalled;
	if (privep->epphy == EP0) {
		privep->stalled = privep->dev->stalled;
	}

	/* Save the result in the request structure */

	privreq->req.result = result;

	/* Callback to the request completion handler */

	privreq->req.callback(&privep->ep, &privreq->req);

	/* Restore the stalled indication */

	privep->stalled = stalled;
}

/*******************************************************************************
 * Name: stm32_req_cancel
 *
 * Description:
 *   Cancel all pending requests for an endpoint
 *
 *******************************************************************************/

static void stm32_req_cancel(struct stm32_ep_s *privep, int16_t status)
{
	if (!stm32_rqempty(privep)) {
		stm32_ep_flush(privep);
	}

	while (!stm32_rqempty(privep)) {
		usbtrace(TRACE_COMPLETE(privep->epphy), (stm32_rqpeek(privep))->req.xfrd);
		stm32_req_complete(privep, status);
	}
}

/*******************************************************************************
 * Name: stm32_ep_findbyaddr
 *
 * Description:
 *   Find the physical endpoint structure corresponding to a logic endpoint
 *   address
 *
 *******************************************************************************/

static struct stm32_ep_s *stm32_ep_findbyaddr(struct stm32_usbdev_s *priv, uint16_t eplog)
{
	struct stm32_ep_s *privep;
	uint8_t epphy = USB_EPNO(eplog);

	if (epphy >= STM32_NENDPOINTS) {
		return NULL;
	}

	/* Is this an IN or an OUT endpoint? */

	if (USB_ISEPIN(eplog)) {
		privep = &priv->epin[epphy];
	} else {
		privep = &priv->epout[epphy];
	}

	/* Return endpoint reference */

	DEBUGASSERT(privep->epphy == epphy);
	return privep;
}

/*******************************************************************************
 * Name: stm32_req_dispatch
 *
 * Description:
 *   Provide unhandled setup actions to the class driver. This is logically part
 *   of the USB interrupt handler.
 *
 *******************************************************************************/

static int stm32_req_dispatch(struct stm32_usbdev_s *priv, const struct usb_ctrlreq_s *ctrl)
{
	int ret = -EIO;

	usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0);
	if (priv->driver) {
		/* Forward to the control request to the class driver implementation */

		ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, priv->ep0data, priv->ep0datlen);
	}

	if (ret < 0) {
		/* Stall on failure */

		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0);
		priv->stalled = true;
	}

	return ret;
}

/*******************************************************************************
 * Name: stm32_usbreset
 *
 * Description:
 *   Reset Usb engine
 *
 *******************************************************************************/

static void stm32_usbreset(struct stm32_usbdev_s *priv)
{
	FAR struct stm32_ep_s *privep;
	uint32_t regval;
	int i;

	/* Clear the Remote Wake-up Signaling */

	regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval &= ~OTGHS_DCTL_RWUSIG;
	stm32_putreg(regval, STM32_OTGHS_DCTL);

	/* Flush the EP0 Tx FIFO */

	stm32_txfifo_flush(OTGHS_GRSTCTL_TXFNUM_D(EP0));

	/* Tell the class driver that we are disconnected. The class
	 * driver should then accept any new configurations.
	 */

	if (priv->driver) {
		CLASS_DISCONNECT(priv->driver, &priv->usbdev);
	}

	/* Mark all endpoints as available */

	priv->epavail[0] = STM32_EP_AVAILABLE;
	priv->epavail[1] = STM32_EP_AVAILABLE;

	/* Disable all end point interrupts */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		/* Disable endpoint interrupts */

		stm32_putreg(0xff, STM32_OTGHS_DIEPINT(i));
		stm32_putreg(0xff, STM32_OTGHS_DOEPINT(i));

		/* Return write requests to the class implementation */

		privep = &priv->epin[i];
		stm32_req_cancel(privep, -ESHUTDOWN);

		/* Reset IN endpoint status */

		privep->stalled = false;

		/* Return read requests to the class implementation */

		privep = &priv->epout[i];
		stm32_req_cancel(privep, -ESHUTDOWN);

		/* Reset endpoint status */

		privep->stalled = false;
	}

	stm32_putreg(0xffffffff, STM32_OTGHS_DAINT);

	/* Mask all device endpoint interrupts except EP0 */

	regval = (OTGHS_DAINT_IEP(EP0) | OTGHS_DAINT_OEP(EP0));
	stm32_putreg(regval, STM32_OTGHS_DAINTMSK);

	/* Unmask OUT interrupts */

	regval = (OTGHS_DOEPMSK_XFRCM | OTGHS_DOEPMSK_STUPM | OTGHS_DOEPMSK_EPDM);
	stm32_putreg(regval, STM32_OTGHS_DOEPMSK);

	/* Unmask IN interrupts */

	regval = (OTGHS_DIEPMSK_XFRCM | OTGHS_DIEPMSK_EPDM | OTGHS_DIEPMSK_TOM);
	stm32_putreg(regval, STM32_OTGHS_DIEPMSK);

	/* Reset device address to 0 */

	stm32_setaddress(priv, 0);
	priv->devstate = DEVSTATE_DEFAULT;
	priv->usbdev.speed = USB_SPEED_FULL;

	/* Re-configure EP0 */

	stm32_ep0_configure(priv);

	/* Setup EP0 to receive SETUP packets */

	stm32_ep0out_ctrlsetup(priv);
}

/*******************************************************************************
 * Name: stm32_ep0out_testmode
 *
 * Description:
 *   Select test mode
 *
 *******************************************************************************/

static inline void stm32_ep0out_testmode(FAR struct stm32_usbdev_s *priv, uint16_t index)
{
	uint8_t testmode;

	testmode = index >> 8;
	switch (testmode) {
	case 1:
		priv->testmode = OTGHS_TESTMODE_J;
		break;

	case 2:
		priv->testmode = OTGHS_TESTMODE_K;
		break;

	case 3:
		priv->testmode = OTGHS_TESTMODE_SE0_NAK;
		break;

	case 4:
		priv->testmode = OTGHS_TESTMODE_PACKET;
		break;

	case 5:
		priv->testmode = OTGHS_TESTMODE_FORCE;
		break;

	default:
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADTESTMODE), testmode);
		priv->dotest = false;
		priv->testmode = OTGHS_TESTMODE_DISABLED;
		priv->stalled = true;
	}

	priv->dotest = true;
	stm32_ep0in_transmitzlp(priv);
}

/*******************************************************************************
 * Name: stm32_ep0out_stdrequest
 *
 * Description:
 *   Handle a stanard request on EP0.  Pick off the things of interest to the
 *   USB device controller driver; pass what is left to the class driver.
 *
 *******************************************************************************/

static inline void stm32_ep0out_stdrequest(struct stm32_usbdev_s *priv, FAR struct stm32_ctrlreq_s *ctrlreq)
{
	FAR struct stm32_ep_s *privep;

	/* Handle standard request */

	switch (ctrlreq->req) {
	case USB_REQ_GETSTATUS: {
		/* type:  device-to-host; recipient = device, interface, endpoint
		 * value: 0
		 * index: zero interface endpoint
		 * len:   2; data = status
		 */

		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS), 0);
		if (!priv->addressed || ctrlreq->len != 2 || USB_REQ_ISOUT(ctrlreq->type) || ctrlreq->value != 0) {
			priv->stalled = true;
		} else {
			switch (ctrlreq->type & USB_REQ_RECIPIENT_MASK) {
			case USB_REQ_RECIPIENT_ENDPOINT: {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), 0);
				privep = stm32_ep_findbyaddr(priv, ctrlreq->index);
				if (!privep) {
					usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0);
					priv->stalled = true;
				} else {
					if (privep->stalled) {
						priv->ep0data[0] = (1 << USB_FEATURE_ENDPOINTHALT);
					} else {
						priv->ep0data[0] = 0;	/* Not stalled */
					}

					priv->ep0data[1] = 0;
					stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
				}
			}
			break;

			case USB_REQ_RECIPIENT_DEVICE: {
				if (ctrlreq->index == 0) {
					usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVGETSTATUS), 0);

					/* Features:  Remote Wakeup and selfpowered */

					priv->ep0data[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED);
					priv->ep0data[0] |= (priv->wakeup << USB_FEATURE_REMOTEWAKEUP);
					priv->ep0data[1] = 0;

					stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
				} else {
					usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADDEVGETSTATUS), 0);
					priv->stalled = true;
				}
			}
			break;

			case USB_REQ_RECIPIENT_INTERFACE: {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0);
				priv->ep0data[0] = 0;
				priv->ep0data[1] = 0;

				stm32_ep0in_setupresponse(priv, priv->ep0data, 2);
			}
			break;

			default: {
				usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0);
				priv->stalled = true;
			}
			break;
			}
		}
	}
	break;

	case USB_REQ_CLEARFEATURE: {
		/* type:  host-to-device; recipient = device, interface or endpoint
		 * value: feature selector
		 * index: zero interface endpoint;
		 * len:   zero, data = none
		 */

		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CLEARFEATURE), 0);
		if (priv->addressed != 0 && ctrlreq->len == 0) {
			uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK;
			if (recipient == USB_REQ_RECIPIENT_ENDPOINT && ctrlreq->value == USB_FEATURE_ENDPOINTHALT && (privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) {
				stm32_ep_clrstall(privep);
				stm32_ep0in_transmitzlp(priv);
			} else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) {
				priv->wakeup = 0;
				stm32_ep0in_transmitzlp(priv);
			} else {
				/* Actually, I think we could just stall here. */

				(void)stm32_req_dispatch(priv, &priv->ctrlreq);
			}
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_SETFEATURE: {
		/* type:  host-to-device; recipient = device, interface, endpoint
		 * value: feature selector
		 * index: zero interface endpoint;
		 * len:   0; data = none
		 */

		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETFEATURE), 0);
		if (priv->addressed != 0 && ctrlreq->len == 0) {
			uint8_t recipient = ctrlreq->type & USB_REQ_RECIPIENT_MASK;
			if (recipient == USB_REQ_RECIPIENT_ENDPOINT && ctrlreq->value == USB_FEATURE_ENDPOINTHALT && (privep = stm32_ep_findbyaddr(priv, ctrlreq->index)) != NULL) {
				stm32_ep_setstall(privep);
				stm32_ep0in_transmitzlp(priv);
			} else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_REMOTEWAKEUP) {
				priv->wakeup = 1;
				stm32_ep0in_transmitzlp(priv);
			} else if (recipient == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == USB_FEATURE_TESTMODE && ((ctrlreq->index & 0xff) == 0)) {
				stm32_ep0out_testmode(priv, ctrlreq->index);
			} else if (priv->configured) {
				/* Actually, I think we could just stall here. */

				(void)stm32_req_dispatch(priv, &priv->ctrlreq);
			} else {
				usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
				priv->stalled = true;
			}
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_SETADDRESS: {
		/* type:  host-to-device; recipient = device
		 * value: device address
		 * index: 0
		 * len:   0; data = none
		 */

		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETADDRESS), ctrlreq->value);
		if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->index == 0 && ctrlreq->len == 0 && ctrlreq->value < 128 && priv->devstate != DEVSTATE_CONFIGURED) {
			/* Save the address.  We cannot actually change to the next address until
			 * the completion of the status phase.
			 */

			stm32_setaddress(priv, (uint16_t)priv->ctrlreq.value[0]);
			stm32_ep0in_transmitzlp(priv);
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_GETDESCRIPTOR:
		/* type:  device-to-host; recipient = device
		 * value: descriptor type and index
		 * index: 0 or language ID;
		 * len:   descriptor len; data = descriptor
		 */

	case USB_REQ_SETDESCRIPTOR:
		/* type:  host-to-device; recipient = device
		 * value: descriptor type and index
		 * index: 0 or language ID;
		 * len:   descriptor len; data = descriptor
		 */

	{
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETDESC), 0);
		if ((ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) {
			(void)stm32_req_dispatch(priv, &priv->ctrlreq);
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSETDESC), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_GETCONFIGURATION:
		/* type:  device-to-host; recipient = device
		 * value: 0;
		 * index: 0;
		 * len:   1; data = configuration value
		 */

	{
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETCONFIG), 0);
		if (priv->addressed && (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->value == 0 && ctrlreq->index == 0 && ctrlreq->len == 1) {
			(void)stm32_req_dispatch(priv, &priv->ctrlreq);
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_SETCONFIGURATION:
		/* type:  host-to-device; recipient = device
		 * value: configuration value
		 * index: 0;
		 * len:   0; data = none
		 */

	{
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETCONFIG), 0);
		if (priv->addressed && (ctrlreq->type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && ctrlreq->index == 0 && ctrlreq->len == 0) {
			/* Give the configuration to the class driver */

			int ret = stm32_req_dispatch(priv, &priv->ctrlreq);

			/* If the class driver accepted the configuration, then mark the
			 * device state as configured (or not, depending on the
			 * configuration).
			 */

			if (ret == OK) {
				uint8_t cfg = (uint8_t)ctrlreq->value;
				if (cfg != 0) {
					priv->devstate = DEVSTATE_CONFIGURED;
					priv->configured = true;
				} else {
					priv->devstate = DEVSTATE_ADDRESSED;
					priv->configured = false;
				}
			}
		} else {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0);
			priv->stalled = true;
		}
	}
	break;

	case USB_REQ_GETINTERFACE:
		/* type:  device-to-host; recipient = interface
		 * value: 0
		 * index: interface;
		 * len:   1; data = alt interface
		 */

	case USB_REQ_SETINTERFACE:
		/* type:  host-to-device; recipient = interface
		 * value: alternate setting
		 * index: interface;
		 * len:   0; data = none
		 */

	{
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF), 0);
		(void)stm32_req_dispatch(priv, &priv->ctrlreq);
	}
	break;

	case USB_REQ_SYNCHFRAME:
		/* type:  device-to-host; recipient = endpoint
		 * value: 0
		 * index: endpoint;
		 * len:   2; data = frame number
		 */

	{
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0);
	}
	break;

	default: {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ), 0);
		priv->stalled = true;
	}
	break;
	}
}

/*******************************************************************************
 * Name: stm32_ep0out_setup
 *
 * Description:
 *   USB Ctrl EP Setup Event. This is logically part of the USB interrupt
 *   handler.  This event occurs when a setup packet is receive on EP0 OUT.
 *
 *******************************************************************************/

static inline void stm32_ep0out_setup(struct stm32_usbdev_s *priv)
{
	struct stm32_ctrlreq_s ctrlreq;

	/* Verify that a SETUP was received */

	if (priv->ep0state != EP0STATE_SETUP_READY) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0NOSETUP), priv->ep0state);
		return;
	}

	/* Terminate any pending requests */

	stm32_req_cancel(&priv->epout[EP0], -EPROTO);
	stm32_req_cancel(&priv->epin[EP0], -EPROTO);

	/* Assume NOT stalled */

	priv->epout[EP0].stalled = false;
	priv->epin[EP0].stalled = false;
	priv->stalled = false;

	/* Starting to process a control request - update state */

	priv->ep0state = EP0STATE_SETUP_PROCESS;

	/* And extract the little-endian 16-bit values to host order */

	ctrlreq.type = priv->ctrlreq.type;
	ctrlreq.req = priv->ctrlreq.req;
	ctrlreq.value = GETUINT16(priv->ctrlreq.value);
	ctrlreq.index = GETUINT16(priv->ctrlreq.index);
	ctrlreq.len = GETUINT16(priv->ctrlreq.len);

	ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", ctrlreq.type, ctrlreq.req, ctrlreq.value, ctrlreq.index, ctrlreq.len);

	/* Check for a standard request */

	if ((ctrlreq.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) {
		/* Dispatch any non-standard requests */

		(void)stm32_req_dispatch(priv, &priv->ctrlreq);
	} else {
		/* Handle standard requests. */

		stm32_ep0out_stdrequest(priv, &ctrlreq);
	}

	/* Check if the setup processing resulted in a STALL */

	if (priv->stalled) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
		stm32_ep0_stall(priv);
	}

	/* Reset state/data associated with thie SETUP request */

	priv->ep0datlen = 0;
}

/*******************************************************************************
 * Name: stm32_epout
 *
 * Description:
 *   This is part of the OUT endpoint interrupt processing.  This function
 *   handles the OUT event for a single endpoint.
 *
 *******************************************************************************/

static inline void stm32_epout(FAR struct stm32_usbdev_s *priv, uint8_t epno)
{
	FAR struct stm32_ep_s *privep;

	/* Endpoint 0 is a special case. */

	if (epno == 0) {
		privep = &priv->epout[EP0];

		/* In the EP0STATE_DATA_OUT state, we are receiving data into the
		 * request buffer.  In that case, we must continue the request
		 * processing.
		 */

		if (priv->ep0state == EP0STATE_DATA_OUT) {
			/* Continue processing data from the EP0 OUT request queue */

			stm32_epout_complete(priv, privep);

			/* If we are not actively processing an OUT request, then we
			 * need to setup to receive the next control request.
			 */

			if (!privep->active) {
				stm32_ep0out_ctrlsetup(priv);
				priv->ep0state = EP0STATE_IDLE;
			}
		}
	}

	/* For other endpoints, the only possibility is that we are continuing
	 * or finishing an OUT request.
	 */

	else if (priv->devstate == DEVSTATE_CONFIGURED) {
		stm32_epout_complete(priv, &priv->epout[epno]);
	}
}

/*******************************************************************************
 * Name: stm32_epout_interrupt
 *
 * Description:
 *   USB OUT endpoint interrupt handler.  The core generates this interrupt when
 *   there is an interrupt is pending on one of the OUT endpoints of the core.
 *   The driver must read the OTGHS DAINT register to determine the exact number
 *   of the OUT endpoint on which the interrupt occurred, and then read the
 *   corresponding OTGHS DOEPINTx register to determine the exact cause of the
 *   interrupt.
 *
 *******************************************************************************/

static inline void stm32_epout_interrupt(FAR struct stm32_usbdev_s *priv)
{
	uint32_t daint;
	uint32_t regval;
	uint32_t doepint;
	int epno;

	/* Get the pending, enabled interrupts for the OUT endpoint from the endpoint
	 * interrupt status register.
	 */

	regval = stm32_getreg(STM32_OTGHS_DAINT);
	regval &= stm32_getreg(STM32_OTGHS_DAINTMSK);
	daint = (regval & OTGHS_DAINT_OEP_MASK) >> OTGHS_DAINT_OEP_SHIFT;

	if (daint == 0) {
		/* We got an interrupt, but there is no unmasked endpoint that caused
		 * it ?!  When this happens, the interrupt flag never gets cleared and
		 * we are stuck in infinite interrupt loop.
		 *
		 * This shouldn't happen if we are diligent about handling timing
		 * issues when masking endpoint interrupts. However, this workaround
		 * avoids infinite loop and allows operation to continue normally.  It
		 * works by clearing each endpoint flags, masked or not.
		 */

		regval = stm32_getreg(STM32_OTGHS_DAINT);
		daint = (regval & OTGHS_DAINT_OEP_MASK) >> OTGHS_DAINT_OEP_SHIFT;

		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTUNEXPECTED), (uint16_t)regval);

		epno = 0;
		while (daint) {
			if ((daint & 1) != 0) {
				regval = stm32_getreg(STM32_OTGHS_DOEPINT(epno));
				ulldbg("DOEPINT(%d) = %08x\n", epno, regval);
				stm32_putreg(0xFF, STM32_OTGHS_DOEPINT(epno));
			}

			epno++;
			daint >>= 1;
		}

		return;
	}

	/* Process each pending IN endpoint interrupt */

	epno = 0;
	while (daint) {
		/* Is an OUT interrupt pending for this endpoint? */

		if ((daint & 1) != 0) {
			/* Yes.. get the OUT endpoint interrupt status */

			doepint = stm32_getreg(STM32_OTGHS_DOEPINT(epno));
			doepint &= stm32_getreg(STM32_OTGHS_DOEPMSK);

			/* Transfer completed interrupt.  This interrupt is trigged when
			 * stm32_rxinterrupt() removes the last packet data from the RxFIFO.
			 * In this case, core internally sets the NAK bit for this endpoint to
			 * prevent it from receiving any more packets.
			 */

			if ((doepint & OTGHS_DOEPINT_XFRC) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_XFRC), (uint16_t)doepint);

				/* Clear the bit in DOEPINTn for this interrupt */

				stm32_putreg(OTGHS_DOEPINT_XFRC, STM32_OTGHS_DOEPINT(epno));

				/* Handle the RX transfer data ready event */

				stm32_epout(priv, epno);
			}

			/* Endpoint disabled interrupt (ignored because this interrrupt is
			 * used in polled mode by the endpoint disable logic).
			 */
#if 1
			/* REVISIT: */
			if ((doepint & OTGHS_DOEPINT_EPDISD) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_EPDISD), (uint16_t)doepint);

				/* Clear the bit in DOEPINTn for this interrupt */

				stm32_putreg(OTGHS_DOEPINT_EPDISD, STM32_OTGHS_DOEPINT(epno));
			}
#endif
			/* Setup Phase Done (control EPs) */

			if ((doepint & OTGHS_DOEPINT_SETUP) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT_SETUP), priv->ep0state);

				/* Handle the receipt of the IN SETUP packets now (OUT setup
				 * packet processing may be delayed until the accompanying
				 * OUT DATA is received)
				 */

				if (priv->ep0state == EP0STATE_SETUP_READY) {
					stm32_ep0out_setup(priv);
				}
				stm32_putreg(OTGHS_DOEPINT_SETUP, STM32_OTGHS_DOEPINT(epno));
			}
		}

		epno++;
		daint >>= 1;
	}
}

/*******************************************************************************
 * Name: stm32_epin_runtestmode
 *
 * Description:
 *   Execute the test mode setup by the SET FEATURE request
 *
 *******************************************************************************/

static inline void stm32_epin_runtestmode(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval &= OTGHS_DCTL_TCTL_MASK;
	regval |= (uint32_t)priv->testmode << OTGHS_DCTL_TCTL_SHIFT;
	stm32_putreg(regval, STM32_OTGHS_DCTL);

	priv->dotest = 0;
	priv->testmode = OTGHS_TESTMODE_DISABLED;
}

/*******************************************************************************
 * Name: stm32_epin
 *
 * Description:
 *   This is part of the IN endpoint interrupt processing.  This function
 *   handles the IN event for a single endpoint.
 *
 *******************************************************************************/

static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno)
{
	FAR struct stm32_ep_s *privep = &priv->epin[epno];

	/* Endpoint 0 is a special case. */

	if (epno == 0) {
		/* In the EP0STATE_DATA_IN state, we are sending data from request
		 * buffer.  In that case, we must continue the request processing.
		 */

		if (priv->ep0state == EP0STATE_DATA_IN) {
			/* Continue processing data from the EP0 OUT request queue */

			stm32_epin_request(priv, privep);

			/* If we are not actively processing an OUT request, then we
			 * need to setup to receive the next control request.
			 */

			if (!privep->active) {
				stm32_ep0out_ctrlsetup(priv);
				priv->ep0state = EP0STATE_IDLE;
			}
		}

		/* Test mode is another special case */

		if (priv->dotest) {
			stm32_epin_runtestmode(priv);
		}
	}

	/* For other endpoints, the only possibility is that we are continuing
	 * or finishing an IN request.
	 */

	else if (priv->devstate == DEVSTATE_CONFIGURED) {
		/* Continue processing data from the endpoint write request queue */

		stm32_epin_request(priv, privep);
	}
}

/****************************************************************************
 * Name: stm32_epin_txfifoempty
 *
 * Description:
 *   TxFIFO empty interrupt handling
 *
 ****************************************************************************/

static inline void stm32_epin_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno)
{
	FAR struct stm32_ep_s *privep = &priv->epin[epno];

	/* Continue processing the write request queue.  This may mean sending
	 * more data from the exisiting request or terminating the current requests
	 * and (perhaps) starting the IN transfer from the next write request.
	 */

	stm32_epin_request(priv, privep);
}

/*******************************************************************************
 * Name: stm32_epin_interrupt
 *
 * Description:
 *   USB IN endpoint interrupt handler.  The core generates this interrupt when
 *   an interrupt is pending on one of the IN endpoints of the core. The driver
 *   must read the OTGHS DAINT register to determine the exact number of the IN
 *   endpoint on which the interrupt occurred, and then read the corresponding
 *   OTGHS DIEPINTx register to determine the exact cause of the interrupt.
 *
 *******************************************************************************/

static inline void stm32_epin_interrupt(FAR struct stm32_usbdev_s *priv)
{
	uint32_t diepint;
	uint32_t daint;
	uint32_t mask;
	uint32_t empty;
	int epno;

	/* Get the pending, enabled interrupts for the IN endpoint from the endpoint
	 * interrupt status register.
	 */

	daint = stm32_getreg(STM32_OTGHS_DAINT);
	daint &= stm32_getreg(STM32_OTGHS_DAINTMSK);
	daint &= OTGHS_DAINT_IEP_MASK;

	if (daint == 0) {
		/* We got an interrupt, but there is no unmasked endpoint that caused
		 * it ?!  When this happens, the interrupt flag never gets cleared and
		 * we are stuck in infinite interrupt loop.
		 *
		 * This shouldn't happen if we are diligent about handling timing
		 * issues when masking endpoint interrupts. However, this workaround
		 * avoids infinite loop and allows operation to continue normally.  It
		 * works by clearing each endpoint flags, masked or not.
		 */

		daint = stm32_getreg(STM32_OTGHS_DAINT);
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPINUNEXPECTED), (uint16_t)daint);

		daint &= OTGHS_DAINT_IEP_MASK;
		epno = 0;

		while (daint) {
			if ((daint & 1) != 0) {
				ulldbg("DIEPINT(%d) = %08x\n", epno, stm32_getreg(STM32_OTGHS_DIEPINT(epno)));
				stm32_putreg(0xFF, STM32_OTGHS_DIEPINT(epno));
			}

			epno++;
			daint >>= 1;
		}

		return;
	}

	/* Process each pending IN endpoint interrupt */

	epno = 0;
	while (daint) {
		/* Is an IN interrupt pending for this endpoint? */

		if ((daint & 1) != 0) {
			/* Get IN interrupt mask register.  Bits 0-6 correspond to enabled
			 * interrupts as will be found in the DIEPINT interrupt status
			 * register.
			 */

			mask = stm32_getreg(STM32_OTGHS_DIEPMSK);

			/* Check if the TxFIFO not empty interrupt is enabled for this
			 * endpoint in the DIEPMSK register.  Bits n corresponds to
			 * endpoint n in the register. That condition corresponds to
			 * bit 7 of the DIEPINT interrupt status register.  There is
			 * no TXFE bit in the mask register, so we fake one here.
			 */

			empty = stm32_getreg(STM32_OTGHS_DIEPEMPMSK);
			if ((empty & OTGHS_DIEPEMPMSK(epno)) != 0) {
				mask |= OTGHS_DIEPINT_TXFE;
			}

			/* Now, read the interrupt status and mask out all disabled
			 * interrupts.
			 */

			diepint = stm32_getreg(STM32_OTGHS_DIEPINT(epno)) & mask;

			/* Decode and process the enabled, pending interrupts */
			/* Transfer completed interrupt */

			if ((diepint & OTGHS_DIEPINT_XFRC) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_XFRC), (uint16_t)diepint);

				/* It is possible that logic may be waiting for a the
				 * TxFIFO to become empty.  We disable the TxFIFO empty
				 * interrupt here; it will be re-enabled if there is still
				 * insufficient space in the TxFIFO.
				 */

				empty &= ~OTGHS_DIEPEMPMSK(epno);
				stm32_putreg(empty, STM32_OTGHS_DIEPEMPMSK);
				stm32_putreg(OTGHS_DIEPINT_XFRC, STM32_OTGHS_DIEPINT(epno));

				/* IN transfer complete */

				stm32_epin(priv, epno);
			}

			/* Timeout condition */

			if ((diepint & OTGHS_DIEPINT_TOC) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TOC), (uint16_t)diepint);
				stm32_putreg(OTGHS_DIEPINT_TOC, STM32_OTGHS_DIEPINT(epno));
			}

			/* IN token received when TxFIFO is empty.  Applies to non-periodic IN
			 * endpoints only.  This interrupt indicates that an IN token was received
			 * when the associated TxFIFO (periodic/non-periodic) was empty. This
			 * interrupt is asserted on the endpoint for which the IN token was
			 * received.
			 */

			if ((diepint & OTGHS_DIEPINT_ITTXFE) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_ITTXFE), (uint16_t)diepint);
				stm32_epin_request(priv, &priv->epin[epno]);
				stm32_putreg(OTGHS_DIEPINT_ITTXFE, STM32_OTGHS_DIEPINT(epno));
			}

			/* IN endpoint NAK effective (ignored as this used only in polled
			 * mode)
			 */
#if 0
			if ((diepint & OTGHS_DIEPINT_INEPNE) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_INEPNE), (uint16_t)diepint);
				stm32_putreg(OTGHS_DIEPINT_INEPNE, STM32_OTGHS_DIEPINT(epno));
			}
#endif
			/* Endpoint disabled interrupt (ignored as this used only in polled
			 * mode)
			 */
#if 0
			if ((diepint & OTGHS_DIEPINT_EPDISD) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_EPDISD), (uint16_t)diepint);
				stm32_putreg(OTGHS_DIEPINT_EPDISD, STM32_OTGHS_DIEPINT(epno));
			}
#endif
			/* Transmit FIFO empty */

			if ((diepint & OTGHS_DIEPINT_TXFE) != 0) {
				usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN_TXFE), (uint16_t)diepint);

				/* If we were waiting for TxFIFO to become empty, the we might have both
				 * XFRC and TXFE interrupts pending.  Since we do the same thing for both
				 * cases, ignore the TXFE if we have already processed the XFRC.
				 */

				if ((diepint & OTGHS_DIEPINT_XFRC) == 0) {
					/* Mask further FIFO empty interrupts.  This will be re-enabled
					 * whenever we need to wait for a FIFO event.
					 */

					empty &= ~OTGHS_DIEPEMPMSK(epno);
					stm32_putreg(empty, STM32_OTGHS_DIEPEMPMSK);

					/* Handle TxFIFO empty */

					stm32_epin_txfifoempty(priv, epno);
				}

				/* Clear the pending TxFIFO empty interrupt */

				stm32_putreg(OTGHS_DIEPINT_TXFE, STM32_OTGHS_DIEPINT(epno));
			}
		}

		epno++;
		daint >>= 1;
	}
}

/*******************************************************************************
 * Name: stm32_resumeinterrupt
 *
 * Description:
 *   Resume/remote wakeup detected interrupt
 *
 *******************************************************************************/

static inline void stm32_resumeinterrupt(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval;

	/* Restart the PHY clock and un-gate USB core clock (HCLK) */

#ifdef CONFIG_USBDEV_LOWPOWER
	regval = stm32_getreg(STM32_OTGHS_PCGCCTL);
	regval &= ~(OTGHS_PCGCCTL_STPPCLK | OTGHS_PCGCCTL_GATEHCLK);
	stm32_putreg(regval, STM32_OTGHS_PCGCCTL);
#endif

	/* Clear remote wake-up signaling */

	regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval &= ~OTGHS_DCTL_RWUSIG;
	stm32_putreg(regval, STM32_OTGHS_DCTL);

	/* Restore full power -- whatever that means for this particular board */

	stm32_usbsuspend((struct usbdev_s *)priv, true);

	/* Notify the class driver of the resume event */

	if (priv->driver) {
		CLASS_RESUME(priv->driver, &priv->usbdev);
	}
}

/*******************************************************************************
 * Name: stm32_suspendinterrupt
 *
 * Description:
 *   USB suspend interrupt
 *
 *******************************************************************************/

static inline void stm32_suspendinterrupt(FAR struct stm32_usbdev_s *priv)
{
#ifdef CONFIG_USBDEV_LOWPOWER
	uint32_t regval;
#endif

	/* Notify the class driver of the suspend event */

	if (priv->driver) {
		CLASS_SUSPEND(priv->driver, &priv->usbdev);
	}
#ifdef CONFIG_USBDEV_LOWPOWER
	/* OTGHS_DSTS_SUSPSTS is set as long as the suspend condition is detected
	 * on USB.  Check if we are still have the suspend condition, that we are
	 * connected to the host, and that we have been configured.
	 */

	regval = stm32_getreg(STM32_OTGHS_DSTS);

	if ((regval & OTGHS_DSTS_SUSPSTS) != 0 && devstate == DEVSTATE_CONFIGURED) {
		/* Switch off OTG HS clocking.  Setting OTGHS_PCGCCTL_STPPCLK stops the
		 * PHY clock.
		 */

		regval = stm32_getreg(STM32_OTGHS_PCGCCTL);
		regval |= OTGHS_PCGCCTL_STPPCLK;
		stm32_putreg(regval, STM32_OTGHS_PCGCCTL);

		/* Setting OTGHS_PCGCCTL_GATEHCLK gate HCLK to modules other than
		 * the AHB Slave and Master and wakeup logic.
		 */

		regval |= OTGHS_PCGCCTL_GATEHCLK;
		stm32_putreg(regval, STM32_OTGHS_PCGCCTL);
	}
#endif

	/* Let the board-specific logic know that we have entered the suspend
	 * state
	 */

	stm32_usbsuspend((FAR struct usbdev_s *)priv, false);
}

/*******************************************************************************
 * Name: stm32_rxinterrupt
 *
 * Description:
 *   RxFIFO non-empty interrupt.  This interrupt indicates that there is at
 *   least one packet pending to be read from the RxFIFO.
 *
 *******************************************************************************/

static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv)
{
	FAR struct stm32_ep_s *privep;
	uint32_t regval;
	int bcnt;
	int epphy;

	/* Disable the Rx status queue level interrupt */

	regval = stm32_getreg(STM32_OTGHS_GINTMSK);
	regval &= ~OTGHS_GINT_RXFLVL;
	stm32_putreg(regval, STM32_OTGHS_GINTMSK);

	/* Get the status from the top of the FIFO */

	regval = stm32_getreg(STM32_OTGHS_GRXSTSP);

	/* Decode status fields */

	epphy = (regval & OTGHS_GRXSTSD_EPNUM_MASK) >> OTGHS_GRXSTSD_EPNUM_SHIFT;
	privep = &priv->epout[epphy];

	/* Handle the RX event according to the packet status field */

	switch (regval & OTGHS_GRXSTSD_PKTSTS_MASK) {
		/* Global OUT NAK.  This indicate that the global OUT NAK bit has taken
		 * effect.
		 *
		 * PKTSTS = Global OUT NAK, BCNT = 0, EPNUM = Don't Care, DPID = Don't
		 * Care.
		 */

	case OTGHS_GRXSTSD_PKTSTS_OUTNAK: {
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTNAK), 0);
	}
	break;

	/* OUT data packet received.
	 *
	 * PKTSTS = DataOUT, BCNT = size of the received data OUT packet,
	 * EPNUM = EPNUM on which the packet was received, DPID = Actual Data PID.
	 */

	case OTGHS_GRXSTSD_PKTSTS_OUTRECVD: {
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTRECVD), epphy);
		bcnt = (regval & OTGHS_GRXSTSD_BCNT_MASK) >> OTGHS_GRXSTSD_BCNT_SHIFT;
		if (bcnt > 0) {
			stm32_epout_receive(privep, bcnt);
		}
	}
	break;

	/* OUT transfer completed.  This indicates that an OUT data transfer for
	 * the specified OUT endpoint has completed. After this entry is popped
	 * from the receive FIFO, the core asserts a Transfer Completed interrupt
	 * on the specified OUT endpoint.
	 *
	 * PKTSTS = Data OUT Transfer Done, BCNT = 0, EPNUM = OUT EP Num on
	 * which the data transfer is complete, DPID = Don't Care.
	 */

	case OTGHS_GRXSTSD_PKTSTS_OUTDONE: {
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OUTDONE), epphy);
	}
	break;

	/* SETUP transaction completed. This indicates that the Setup stage for
	 * the specified endpoint has completed and the Data stage has started.
	 * After this entry is popped from the receive FIFO, the core asserts a
	 * Setup interrupt on the specified control OUT endpoint (triggers an
	 * interrupt).
	 *
	 * PKTSTS = Setup Stage Done, BCNT = 0, EPNUM = Control EP Num,
	 * DPID = Don't Care.
	 */

	case OTGHS_GRXSTSD_PKTSTS_SETUPDONE: {
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPDONE), epphy);
	}
	break;

	/* SETUP data packet received.  This indicates that a SETUP packet for the
	 * specified endpoint is now available for reading from the receive FIFO.
	 *
	 * PKTSTS = SETUP, BCNT = 8, EPNUM = Control EP Num, DPID = D0.
	 */

	case OTGHS_GRXSTSD_PKTSTS_SETUPRECVD: {
		uint16_t datlen;

		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETUPRECVD), epphy);

		/* Read EP0 setup data.  NOTE:  If multiple SETUP packets are received,
		 * the last one overwrites the previous setup packets and only that
		 * last SETUP packet will be processed.
		 */

		stm32_rxfifo_read(&priv->epout[EP0], (FAR uint8_t *)&priv->ctrlreq, USB_SIZEOF_CTRLREQ);

		/* Was this an IN or an OUT SETUP packet.  If it is an OUT SETUP,
		 * then we need to wait for the completion of the data phase to
		 * process the setup command.  If it is an IN SETUP packet, then
		 * we must processing the command BEFORE we enter the DATA phase.
		 *
		 * If the data associated with the OUT SETUP packet is zero length,
		 * then, of course, we don't need to wait.
		 */

		datlen = GETUINT16(priv->ctrlreq.len);
		if (USB_REQ_ISOUT(priv->ctrlreq.type) && datlen > 0) {
			/* Clear NAKSTS so that we can receive the data */

			regval = stm32_getreg(STM32_OTGHS_DOEPCTL0);
			regval |= OTGHS_DOEPCTL0_CNAK;
			stm32_putreg(regval, STM32_OTGHS_DOEPCTL0);

			/* Wait for the data phase. */

			priv->ep0state = EP0STATE_SETUP_OUT;
		} else {
			/* We can process the setup data as soon as SETUP done word is
			 * popped of the RxFIFO.
			 */

			priv->ep0state = EP0STATE_SETUP_READY;
		}
	}
	break;

	default: {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), (regval & OTGHS_GRXSTSD_PKTSTS_MASK) >> OTGHS_GRXSTSD_PKTSTS_SHIFT);
	}
	break;
	}

	/* Enable the Rx Status Queue Level interrupt */

	regval = stm32_getreg(STM32_OTGHS_GINTMSK);
	regval |= OTGHS_GINT_RXFLVL;
	stm32_putreg(regval, STM32_OTGHS_GINTMSK);
}

/*******************************************************************************
 * Name: stm32_enuminterrupt
 *
 * Description:
 *   Enumeration done interrupt
 *
 *******************************************************************************/

static inline void stm32_enuminterrupt(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval;

	/* Activate EP0 */

	stm32_ep0in_activate();

	/* Set USB turn-around time for the full speed device with internal PHY interface. */

	regval = stm32_getreg(STM32_OTGHS_GUSBCFG);
	regval &= ~OTGHS_GUSBCFG_TRDT_MASK;
	regval |= OTGHS_GUSBCFG_TRDT(5);
	stm32_putreg(regval, STM32_OTGHS_GUSBCFG);
}

/*******************************************************************************
 * Name: stm32_isocininterrupt
 *
 * Description:
 *   Incomplete isochronous IN transfer interrupt.  Assertion of the incomplete
 *   isochronous IN transfer interrupt indicates an incomplete isochronous IN
 *   transfer on at least one of the isochronous IN endpoints.
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocininterrupt(FAR struct stm32_usbdev_s *priv)
{
	int i;

	/* The application must read the endpoint control register for all isochronous
	 * IN endpoints to detect endpoints with incomplete IN data transfers.
	 */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		/* Is this an isochronous IN endpoint? */

		privep = &priv->epin[i];
		if (privep->eptype != USB_EP_ATTR_XFER_ISOC) {
			/* No... keep looking */

			continue;
		}

		/* Is there an active read request on the isochronous OUT endpoint? */

		if (!privep->active) {
			/* No.. the endpoint is not actively transmitting data */

			continue;
		}

		/* Check if this is the endpoint that had the incomplete transfer */

		regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
		doepctl = stm32_getreg(regaddr);
		dsts = stm32_getreg(STM32_OTGHS_DSTS);

		/* EONUM = 0:even frame, 1:odd frame
		 * SOFFN = Frame number of the received SOF
		 */

		eonum = ((doepctl & OTGHS_DIEPCTL_EONUM) != 0);
		soffn = ((dsts & OTGHS_DSTS_SOFFN0) != 0);

		if (eonum != soffn) {
			/* Not this endpoint */

			continue;
		}

		/* For isochronous IN endpoints with incomplete transfers,
		 * the application must discard the data in the memory and
		 * disable the endpoint.
		 */

		stm32_req_complete(privep, -EIO);
#warning "Will clear OTGHS_DIEPCTL_USBAEP too"
		stm32_epin_disable(privep);
		break;
	}
}
#endif

/*******************************************************************************
 * Name: stm32_isocoutinterrupt
 *
 * Description:
 *   Incomplete periodic transfer interrupt
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_ISOCHRONOUS
static inline void stm32_isocoutinterrupt(FAR struct stm32_usbdev_s *priv)
{
	FAR struct stm32_ep_s *privep;
	FAR struct stm32_req_s *privreq;
	uint32_t regaddr;
	uint32_t doepctl;
	uint32_t dsts;
	bool eonum;
	bool soffn;

	/* When it receives an IISOOXFR interrupt, the application must read the
	 * control registers of all isochronous OUT endpoints to determine which
	 * endpoints had an incomplete transfer in the current microframe. An
	 * endpoint transfer is incomplete if both the following conditions are true:
	 *
	 *   DOEPCTLx:EONUM = DSTS:SOFFN[0], and
	 *   DOEPCTLx:EPENA = 1
	 */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		/* Is this an isochronous OUT endpoint? */

		privep = &priv->epout[i];
		if (privep->eptype != USB_EP_ATTR_XFER_ISOC) {
			/* No... keep looking */

			continue;
		}

		/* Is there an active read request on the isochronous OUT endpoint? */

		if (!privep->active) {
			/* No.. the endpoint is not actively transmitting data */

			continue;
		}

		/* Check if this is the endpoint that had the incomplete transfer */

		regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
		doepctl = stm32_getreg(regaddr);
		dsts = stm32_getreg(STM32_OTGHS_DSTS);

		/* EONUM = 0:even frame, 1:odd frame
		 * SOFFN = Frame number of the received SOF
		 */

		eonum = ((doepctl & OTGHS_DOEPCTL_EONUM) != 0);
		soffn = ((dsts & OTGHS_DSTS_SOFFN0) != 0);

		if (eonum != soffn) {
			/* Not this endpoint */

			continue;
		}

		/* For isochronous OUT endpoints with incomplete transfers,
		 * the application must discard the data in the memory and
		 * disable the endpoint.
		 */

		stm32_req_complete(privep, -EIO);
#warning "Will clear OTGHS_DOEPCTL_USBAEP too"
		stm32_epout_disable(privep);
		break;
	}
}
#endif

/*******************************************************************************
 * Name: stm32_sessioninterrupt
 *
 * Description:
 *   Session request/new session detected interrupt
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_sessioninterrupt(FAR struct stm32_usbdev_s *priv)
{
#warning "Missing logic"
}
#endif

/*******************************************************************************
 * Name: stm32_otginterrupt
 *
 * Description:
 *   OTG interrupt
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_VBUSSENSING
static inline void stm32_otginterrupt(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval;

	/* Check for session end detected */

	regval = stm32_getreg(STM32_OTGHS_GOTGINT);
	if ((regval & OTGHS_GOTGINT_SEDET) != 0) {
#warning "Missing logic"
	}

	/* Clear OTG interrupt */

	stm32_putreg(retval, STM32_OTGHS_GOTGINT);
}
#endif

/*******************************************************************************
 * Name: stm32_usbinterrupt
 *
 * Description:
 *   USB interrupt handler
 *
 *******************************************************************************/

static int stm32_usbinterrupt(int irq, FAR void *context)
{
	/* At present, there is only a single OTG HS device support. Hence it is
	 * pre-allocated as g_otghsdev.  However, in most code, the private data
	 * structure will be referenced using the 'priv' pointer (rather than the
	 * global data) in order to simplify any future support for multiple devices.
	 */

	FAR struct stm32_usbdev_s *priv = &g_otghsdev;
	uint32_t regval;

	usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_USB), 0);

	/* Assure that we are in device mode */

	DEBUGASSERT((stm32_getreg(STM32_OTGHS_GINTSTS) & OTGHS_GINTSTS_CMOD) == OTGHS_GINTSTS_DEVMODE);

	/* Get the state of all enabled interrupts.  We will do this repeatedly
	 * some interrupts (like RXFLVL) will generate additional interrupting
	 * events.
	 */

	for (;;) {
		/* Get the set of pending, un-masked interrupts */

		regval = stm32_getreg(STM32_OTGHS_GINTSTS);
		regval &= stm32_getreg(STM32_OTGHS_GINTMSK);

		/* Break out of the loop when there are no further pending (and
		 * unmasked) interrupts to be processes.
		 */

		if (regval == 0) {
			break;
		}
		usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_INTPENDING), (uint16_t)regval);

		/* OUT endpoint interrupt. The core sets this bit to indicate that an
		 * interrupt is pending on one of the OUT endpoints of the core.
		 */

		if ((regval & OTGHS_GINT_OEP) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUT), (uint16_t)regval);
			stm32_epout_interrupt(priv);
			stm32_putreg(OTGHS_GINT_OEP, STM32_OTGHS_GINTSTS);
		}

		/* IN endpoint interrupt.  The core sets this bit to indicate that
		 * an interrupt is pending on one of the IN endpoints of the core.
		 */

		if ((regval & OTGHS_GINT_IEP) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPIN), (uint16_t)regval);
			stm32_epin_interrupt(priv);
			stm32_putreg(OTGHS_GINT_IEP, STM32_OTGHS_GINTSTS);
		}

		/* Host/device mode mismatch error interrupt */

#ifdef CONFIG_DEBUG_USB
		if ((regval & OTGHS_GINT_MMIS) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_MISMATCH), (uint16_t)regval);
			stm32_putreg(OTGHS_GINT_MMIS, STM32_OTGHS_GINTSTS);
		}
#endif

		/* Resume/remote wakeup detected interrupt */

		if ((regval & OTGHS_GINT_WKUP) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WAKEUP), (uint16_t)regval);
			stm32_resumeinterrupt(priv);
			stm32_putreg(OTGHS_GINT_WKUP, STM32_OTGHS_GINTSTS);
		}

		/* USB suspend interrupt */

		if ((regval & OTGHS_GINT_USBSUSP) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSPEND), (uint16_t)regval);
			stm32_suspendinterrupt(priv);
			stm32_putreg(OTGHS_GINT_USBSUSP, STM32_OTGHS_GINTSTS);
		}

		/* Start of frame interrupt */

#ifdef CONFIG_USBDEV_SOFINTERRUPT
		if ((regval & OTGHS_GINT_SOF) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SOF), (uint16_t)regval);
			stm32_putreg(OTGHS_GINT_SOF, STM32_OTGHS_GINTSTS);
		}
#endif

		/* RxFIFO non-empty interrupt.  Indicates that there is at least one
		 * packet pending to be read from the RxFIFO.
		 */

		if ((regval & OTGHS_GINT_RXFLVL) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RXFIFO), (uint16_t)regval);
			stm32_rxinterrupt(priv);
			stm32_putreg(OTGHS_GINT_RXFLVL, STM32_OTGHS_GINTSTS);
		}

		/* USB reset interrupt */

		if ((regval & OTGHS_GINT_USBRST) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DEVRESET), (uint16_t)regval);

			/* Perform the device reset */

			stm32_usbreset(priv);
			usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0);
			stm32_putreg(OTGHS_GINT_USBRST, STM32_OTGHS_GINTSTS);
			return OK;
		}

		/* Enumeration done interrupt */

		if ((regval & OTGHS_GINT_ENUMDNE) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ENUMDNE), (uint16_t)regval);
			stm32_enuminterrupt(priv);
			stm32_putreg(OTGHS_GINT_ENUMDNE, STM32_OTGHS_GINTSTS);
		}

		/* Incomplete isochronous IN transfer interrupt.  When the core finds
		 * non-empty any of the isochronous IN endpoint FIFOs scheduled for
		 * the current frame non-empty, the core generates an IISOIXFR
		 * interrupt.
		 */

#ifdef CONFIG_USBDEV_ISOCHRONOUS
		if ((regval & OTGHS_GINT_IISOIXFR) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOIXFR), (uint16_t)regval);
			stm32_isocininterrupt(priv);
			stm32_putreg(OTGHS_GINT_IISOIXFR, STM32_OTGHS_GINTSTS);
		}

		/* Incomplete isochronous OUT transfer.  For isochronous OUT
		 * endpoints, the XFRC interrupt may not always be asserted. If the
		 * core drops isochronous OUT data packets, the application could fail
		 * to detect the XFRC interrupt.  The incomplete Isochronous OUT data
		 * interrupt indicates that an XFRC interrupt was not asserted on at
		 * least one of the isochronous OUT endpoints. At this point, the
		 * endpoint with the incomplete transfer remains enabled, but no active
		 * transfers remain in progress on this endpoint on the USB.
		 */

		if ((regval & OTGHS_GINT_IISOOXFR) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IISOOXFR), (uint16_t)regval);
			stm32_isocoutinterrupt(priv);
			stm32_putreg(OTGHS_GINT_IISOOXFR, STM32_OTGHS_GINTSTS);
		}
#endif

		/* Session request/new session detected interrupt */

#ifdef CONFIG_USBDEV_VBUSSENSING
		if ((regval & OTGHS_GINT_SRQ) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SRQ), (uint16_t)regval);
			stm32_sessioninterrupt(priv);
			stm32_putreg(OTGHS_GINT_SRQ, STM32_OTGHS_GINTSTS);
		}

		/* OTG interrupt */

		if ((regval & OTGHS_GINT_OTG) != 0) {
			usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_OTG), (uint16_t)regval);
			stm32_otginterrupt(priv);
			stm32_putreg(OTGHS_GINT_OTG, STM32_OTGHS_GINTSTS);
		}
#endif
	}

	usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_USB), 0);
	return OK;
}

/*******************************************************************************
 * Endpoint operations
 *******************************************************************************/

/*******************************************************************************
 * Name: stm32_enablegonak
 *
 * Description:
 *   Enable global OUT NAK mode
 *
 *******************************************************************************/

static void stm32_enablegonak(FAR struct stm32_ep_s *privep)
{
	uint32_t regval;

	/* First, make sure that there is no GNOAKEFF interrupt pending. */

#if 0
	stm32_putreg(OTGHS_GINT_GONAKEFF, STM32_OTGHS_GINTSTS);
#endif

	/* Enable Global OUT NAK mode in the core. */

	regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval |= OTGHS_DCTL_SGONAK;
	stm32_putreg(regval, STM32_OTGHS_DCTL);

#if 0
	/* Wait for the GONAKEFF interrupt that indicates that the OUT NAK
	 * mode is in effect.  When the interrupt handler pops the OUTNAK word
	 * from the RxFIFO, the core sets the GONAKEFF interrupt.
	 */

	while ((stm32_getreg(STM32_OTGHS_GINTSTS) & OTGHS_GINT_GONAKEFF) == 0) ;
	stm32_putreg(OTGHS_GINT_GONAKEFF, STM32_OTGHS_GINTSTS);

#else
	/* Since we are in the interrupt handler, we cannot wait inline for the
	 * GONAKEFF because it cannot occur until service th RXFLVL global interrupt
	 * and pop the OUTNAK word from the RxFIFO.
	 *
	 * Perhaps it is sufficient to wait for Global OUT NAK status to be reported
	 * in OTGHS DCTL register?
	 */

	while ((stm32_getreg(STM32_OTGHS_DCTL) & OTGHS_DCTL_GONSTS) == 0) ;
#endif
}

/*******************************************************************************
 * Name: stm32_disablegonak
 *
 * Description:
 *   Disable global OUT NAK mode
 *
 *******************************************************************************/

static void stm32_disablegonak(FAR struct stm32_ep_s *privep)
{
	uint32_t regval;

	/* Set the "Clear the Global OUT NAK bit" to disable global OUT NAK mode */

	regval = stm32_getreg(STM32_OTGHS_DCTL);
	regval |= OTGHS_DCTL_CGONAK;
	stm32_putreg(regval, STM32_OTGHS_DCTL);
}

/*******************************************************************************
 * Name: stm32_epout_configure
 *
 * Description:
 *   Configure an OUT endpoint, making it usable
 *
 * Input Parameters:
 *   privep    - a pointer to an internal endpoint structure
 *   eptype    - The type of the endpoint
 *   maxpacket - The max packet size of the endpoint
 *
 *******************************************************************************/

static int stm32_epout_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, uint16_t maxpacket)
{
	uint32_t mpsiz;
	uint32_t regaddr;
	uint32_t regval;

	usbtrace(TRACE_EPCONFIGURE, privep->epphy);

	/* For EP0, the packet size is encoded */

	if (privep->epphy == EP0) {
		DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL);

		/* Map the size in bytes to the encoded value in the register */

		switch (maxpacket) {
		case 8:
			mpsiz = OTGHS_DOEPCTL0_MPSIZ_8;
			break;

		case 16:
			mpsiz = OTGHS_DOEPCTL0_MPSIZ_16;
			break;

		case 32:
			mpsiz = OTGHS_DOEPCTL0_MPSIZ_32;
			break;

		case 64:
			mpsiz = OTGHS_DOEPCTL0_MPSIZ_64;
			break;

		default:
			udbg("Unsupported maxpacket: %d\n", maxpacket);
			return -EINVAL;
		}
	}

	/* For other endpoints, the packet size is in bytes */

	else {
		mpsiz = (maxpacket << OTGHS_DOEPCTL_MPSIZ_SHIFT);
	}

	/* If the endpoint is already active don't change the endpoint control
	 * register.
	 */

	regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	if ((regval & OTGHS_DOEPCTL_USBAEP) == 0) {
		if (regval & OTGHS_DOEPCTL_NAKSTS) {
			regval |= OTGHS_DOEPCTL_CNAK;
		}

		regval &= ~(OTGHS_DOEPCTL_MPSIZ_MASK | OTGHS_DOEPCTL_EPTYP_MASK);
		regval |= mpsiz;
		regval |= (eptype << OTGHS_DOEPCTL_EPTYP_SHIFT);
		regval |= (OTGHS_DOEPCTL_SD0PID | OTGHS_DOEPCTL_USBAEP);
		stm32_putreg(regval, regaddr);

		/* Save the endpoint configuration */

		privep->ep.maxpacket = maxpacket;
		privep->eptype = eptype;
		privep->stalled = false;
	}

	/* Enable the interrupt for this endpoint */

	regval = stm32_getreg(STM32_OTGHS_DAINTMSK);
	regval |= OTGHS_DAINT_OEP(privep->epphy);
	stm32_putreg(regval, STM32_OTGHS_DAINTMSK);
	return OK;
}

/*******************************************************************************
 * Name: stm32_epin_configure
 *
 * Description:
 *   Configure an IN endpoint, making it usable
 *
 * Input Parameters:
 *   privep    - a pointer to an internal endpoint structure
 *   eptype    - The type of the endpoint
 *   maxpacket - The max packet size of the endpoint
 *
 *******************************************************************************/

static int stm32_epin_configure(FAR struct stm32_ep_s *privep, uint8_t eptype, uint16_t maxpacket)
{
	uint32_t mpsiz;
	uint32_t regaddr;
	uint32_t regval;

	usbtrace(TRACE_EPCONFIGURE, privep->epphy);

	/* For EP0, the packet size is encoded */

	if (privep->epphy == EP0) {
		DEBUGASSERT(eptype == USB_EP_ATTR_XFER_CONTROL);

		/* Map the size in bytes to the encoded value in the register */

		switch (maxpacket) {
		case 8:
			mpsiz = OTGHS_DIEPCTL0_MPSIZ_8;
			break;

		case 16:
			mpsiz = OTGHS_DIEPCTL0_MPSIZ_16;
			break;

		case 32:
			mpsiz = OTGHS_DIEPCTL0_MPSIZ_32;
			break;

		case 64:
			mpsiz = OTGHS_DIEPCTL0_MPSIZ_64;
			break;

		default:
			udbg("Unsupported maxpacket: %d\n", maxpacket);
			return -EINVAL;
		}
	}

	/* For other endpoints, the packet size is in bytes */

	else {
		mpsiz = (maxpacket << OTGHS_DIEPCTL_MPSIZ_SHIFT);
	}

	/* If the endpoint is already active don't change the endpoint control
	 * register.
	 */

	regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	if ((regval & OTGHS_DIEPCTL_USBAEP) == 0) {
		if (regval & OTGHS_DIEPCTL_NAKSTS) {
			regval |= OTGHS_DIEPCTL_CNAK;
		}

		regval &= ~(OTGHS_DIEPCTL_MPSIZ_MASK | OTGHS_DIEPCTL_EPTYP_MASK | OTGHS_DIEPCTL_TXFNUM_MASK);
		regval |= mpsiz;
		regval |= (eptype << OTGHS_DIEPCTL_EPTYP_SHIFT);
		regval |= (eptype << OTGHS_DIEPCTL_TXFNUM_SHIFT);
		regval |= (OTGHS_DIEPCTL_SD0PID | OTGHS_DIEPCTL_USBAEP);
		stm32_putreg(regval, regaddr);

		/* Save the endpoint configuration */

		privep->ep.maxpacket = maxpacket;
		privep->eptype = eptype;
		privep->stalled = false;
	}

	/* Enable the interrupt for this endpoint */

	regval = stm32_getreg(STM32_OTGHS_DAINTMSK);
	regval |= OTGHS_DAINT_IEP(privep->epphy);
	stm32_putreg(regval, STM32_OTGHS_DAINTMSK);

	return OK;
}

/*******************************************************************************
 * Name: stm32_ep_configure
 *
 * Description:
 *   Configure endpoint, making it usable
 *
 * Input Parameters:
 *   ep   - the struct usbdev_ep_s instance obtained from allocep()
 *   desc - A struct usb_epdesc_s instance describing the endpoint
 *   last - true if this this last endpoint to be configured.  Some hardware
 *          needs to take special action when all of the endpoints have been
 *          configured.
 *
 *******************************************************************************/

static int stm32_ep_configure(FAR struct usbdev_ep_s *ep, FAR const struct usb_epdesc_s *desc, bool last)
{
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
	uint16_t maxpacket;
	uint8_t eptype;
	int ret;

	usbtrace(TRACE_EPCONFIGURE, privep->epphy);
	DEBUGASSERT(desc->addr == ep->eplog);

	/* Initialize EP capabilities */

	maxpacket = GETUINT16(desc->mxpacketsize);
	eptype = desc->attr & USB_EP_ATTR_XFERTYPE_MASK;

	/* Setup Endpoint Control Register */

	if (privep->isin) {
		ret = stm32_epin_configure(privep, eptype, maxpacket);
	} else {
		ret = stm32_epout_configure(privep, eptype, maxpacket);
	}

	return ret;
}

/*******************************************************************************
 * Name: stm32_ep0_configure
 *
 * Description:
 *   Reset Usb engine
 *
 *******************************************************************************/

static void stm32_ep0_configure(FAR struct stm32_usbdev_s *priv)
{
	/* Enable EP0 IN and OUT */

	(void)stm32_epin_configure(&priv->epin[EP0], USB_EP_ATTR_XFER_CONTROL, CONFIG_USBDEV_EP0_MAXSIZE);
	(void)stm32_epout_configure(&priv->epout[EP0], USB_EP_ATTR_XFER_CONTROL, CONFIG_USBDEV_EP0_MAXSIZE);
}

/*******************************************************************************
 * Name: stm32_epout_disable
 *
 * Description:
 *   Diable an OUT endpoint will no longer be used
 *
 *******************************************************************************/

static void stm32_epout_disable(FAR struct stm32_ep_s *privep)
{
	uint32_t regaddr;
	uint32_t regval;
	irqstate_t flags;

	usbtrace(TRACE_EPDISABLE, privep->epphy);

	/* Is this an IN or an OUT endpoint */

	/* Before disabling any OUT endpoint, the application must enable
	 * Global OUT NAK mode in the core.
	 */

	flags = irqsave();
	stm32_enablegonak(privep);

	/* Disable the required OUT endpoint by setting the EPDIS and SNAK bits
	 * int DOECPTL register.
	 */

	regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	regval &= ~OTGHS_DOEPCTL_USBAEP;
	regval |= (OTGHS_DOEPCTL_EPDIS | OTGHS_DOEPCTL_SNAK);
	stm32_putreg(regval, regaddr);

	/* Wait for the EPDISD interrupt which indicates that the OUT
	 * endpoint is completely disabled.
	 */

#if 0							/* Doesn't happen */
	regaddr = STM32_OTGHS_DOEPINT(privep->epphy);
	while ((stm32_getreg(regaddr) & OTGHS_DOEPINT_EPDISD) == 0) ;
#else
	/* REVISIT: */
	up_udelay(10);
#endif

	/* Clear the EPDISD interrupt indication */

	stm32_putreg(OTGHS_DOEPINT_EPDISD, STM32_OTGHS_DOEPINT(privep->epphy));

	/* Then disble the Global OUT NAK mode to continue receiving data
	 * from other non-disabled OUT endpoints.
	 */

	stm32_disablegonak(privep);

	/* Disable endpoint interrupts */

	regval = stm32_getreg(STM32_OTGHS_DAINTMSK);
	regval &= ~OTGHS_DAINT_OEP(privep->epphy);
	stm32_putreg(regval, STM32_OTGHS_DAINTMSK);

	/* Cancel any queued read requests */

	stm32_req_cancel(privep, -ESHUTDOWN);

	irqrestore(flags);
}

/*******************************************************************************
 * Name: stm32_epin_disable
 *
 * Description:
 *   Disable an IN endpoint when it will no longer be used
 *
 *******************************************************************************/

static void stm32_epin_disable(FAR struct stm32_ep_s *privep)
{
	uint32_t regaddr;
	uint32_t regval;
	irqstate_t flags;

	usbtrace(TRACE_EPDISABLE, privep->epphy);

	/* After USB reset, the endpoint will already be deactivated by the
	 * hardware. Trying to disable again will just hang in the wait.
	 */

	regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	if ((regval & OTGHS_DIEPCTL_USBAEP) == 0) {
		return;
	}

	/* This INEPNE wait logic is suggested by reference manual, but seems
	 * to get stuck to infinite loop.
	 */

#if 0
	/* Make sure that there is no pending IPEPNE interrupt (because we are
	 * to poll this bit below).
	 */

	stm32_putreg(OTGHS_DIEPINT_INEPNE, STM32_OTGHS_DIEPINT(privep->epphy));

	/* Set the endpoint in NAK mode */

	regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	regval &= ~OTGHS_DIEPCTL_USBAEP;
	regval |= (OTGHS_DIEPCTL_EPDIS | OTGHS_DIEPCTL_SNAK);
	stm32_putreg(regval, regaddr);

	/* Wait for the INEPNE interrupt that indicates that we are now in NAK mode */

	regaddr = STM32_OTGHS_DIEPINT(privep->epphy);
	while ((stm32_getreg(regaddr) & OTGHS_DIEPINT_INEPNE) == 0) ;

	/* Clear the INEPNE interrupt indication */

	stm32_putreg(OTGHS_DIEPINT_INEPNE, regaddr);
#endif

	/* Deactivate and disable the endpoint by setting the EPDIS and SNAK bits
	 * the DIEPCTLx register.
	 */

	flags = irqsave();
	regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	regval &= ~OTGHS_DIEPCTL_USBAEP;
	regval |= (OTGHS_DIEPCTL_EPDIS | OTGHS_DIEPCTL_SNAK);
	stm32_putreg(regval, regaddr);

	/* Wait for the EPDISD interrupt which indicates that the IN
	 * endpoint is completely disabled.
	 */

	regaddr = STM32_OTGHS_DIEPINT(privep->epphy);
	while ((stm32_getreg(regaddr) & OTGHS_DIEPINT_EPDISD) == 0) ;

	/* Clear the EPDISD interrupt indication */

	stm32_putreg(OTGHS_DIEPINT_EPDISD, stm32_getreg(regaddr));

	/* Flush any data remaining in the TxFIFO */

	stm32_txfifo_flush(OTGHS_GRSTCTL_TXFNUM_D(privep->epphy));

	/* Disable endpoint interrupts */

	regval = stm32_getreg(STM32_OTGHS_DAINTMSK);
	regval &= ~OTGHS_DAINT_IEP(privep->epphy);
	stm32_putreg(regval, STM32_OTGHS_DAINTMSK);

	/* Cancel any queued write requests */

	stm32_req_cancel(privep, -ESHUTDOWN);
	irqrestore(flags);
}

/*******************************************************************************
 * Name: stm32_ep_disable
 *
 * Description:
 *   The endpoint will no longer be used
 *
 *******************************************************************************/

static int stm32_ep_disable(FAR struct usbdev_ep_s *ep)
{
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;

#ifdef CONFIG_DEBUG
	if (!ep) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return -EINVAL;
	}
#endif

	usbtrace(TRACE_EPDISABLE, privep->epphy);

	/* Is this an IN or an OUT endpoint */

	if (privep->isin) {
		/* Disable the IN endpoint */

		stm32_epin_disable(privep);
	} else {
		/* Disable the OUT endpoint */

		stm32_epout_disable(privep);
	}

	return OK;
}

/*******************************************************************************
 * Name: stm32_ep_allocreq
 *
 * Description:
 *   Allocate an I/O request
 *
 *******************************************************************************/

static FAR struct usbdev_req_s *stm32_ep_allocreq(FAR struct usbdev_ep_s *ep)
{
	FAR struct stm32_req_s *privreq;

#ifdef CONFIG_DEBUG
	if (!ep) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return NULL;
	}
#endif

	usbtrace(TRACE_EPALLOCREQ, ((FAR struct stm32_ep_s *)ep)->epphy);

	privreq = (FAR struct stm32_req_s *)kmm_malloc(sizeof(struct stm32_req_s));
	if (!privreq) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0);
		return NULL;
	}

	memset(privreq, 0, sizeof(struct stm32_req_s));
	return &privreq->req;
}

/*******************************************************************************
 * Name: stm32_ep_freereq
 *
 * Description:
 *   Free an I/O request
 *
 *******************************************************************************/

static void stm32_ep_freereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
	FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req;

#ifdef CONFIG_DEBUG
	if (!ep || !req) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return;
	}
#endif

	usbtrace(TRACE_EPFREEREQ, ((FAR struct stm32_ep_s *)ep)->epphy);
	kmm_free(privreq);
}

/*******************************************************************************
 * Name: stm32_ep_allocbuffer
 *
 * Description:
 *   Allocate an I/O buffer
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_DMA
static void *stm32_ep_allocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes)
{
	usbtrace(TRACE_EPALLOCBUFFER, privep->epphy);

#ifdef CONFIG_USBDEV_DMAMEMORY
	return usbdev_dma_alloc(bytes);
#else
	return kmm_malloc(bytes);
#endif
}
#endif

/*******************************************************************************
 * Name: stm32_ep_freebuffer
 *
 * Description:
 *   Free an I/O buffer
 *
 *******************************************************************************/

#ifdef CONFIG_USBDEV_DMA
static void stm32_ep_freebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
	usbtrace(TRACE_EPFREEBUFFER, privep->epphy);

#ifdef CONFIG_USBDEV_DMAMEMORY
	usbdev_dma_free(buf);
#else
	kmm_free(buf);
#endif
}
#endif

/*******************************************************************************
 * Name: stm32_ep_submit
 *
 * Description:
 *   Submit an I/O request to the endpoint
 *
 *******************************************************************************/

static int stm32_ep_submit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
	FAR struct stm32_req_s *privreq = (FAR struct stm32_req_s *)req;
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
	FAR struct stm32_usbdev_s *priv;
	irqstate_t flags;
	int ret = OK;

	/* Some sanity checking */

#ifdef CONFIG_DEBUG
	if (!req || !req->callback || !req->buf || !ep) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		ullvdbg("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep);
		return -EINVAL;
	}
#endif

	usbtrace(TRACE_EPSUBMIT, privep->epphy);
	priv = privep->dev;

#ifdef CONFIG_DEBUG
	if (!priv->driver) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED), priv->usbdev.speed);
		return -ESHUTDOWN;
	}
#endif

	/* Handle the request from the class driver */

	req->result = -EINPROGRESS;
	req->xfrd = 0;

	/* Disable Interrupts */

	flags = irqsave();

	/* If we are stalled, then drop all requests on the floor */

	if (privep->stalled) {
		ret = -EBUSY;
	} else {
		/* Add the new request to the request queue for the endpoint. */

		if (stm32_req_addlast(privep, privreq) && !privep->active) {
			/* If a request was added to an IN endpoint, then attempt to send
			 * the request data buffer now.
			 */

			if (privep->isin) {
				usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);

				/* If the endpoint is not busy with another write request,
				 * then process the newly received write request now.
				 */

				if (!privep->active) {
					stm32_epin_request(priv, privep);
				}
			}

			/* If the request was added to an OUT endoutput, then attempt to
			 * setup a read into the request data buffer now (this will, of
			 * course, fail if there is already a read in place).
			 */

			else {
				usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
				stm32_epout_request(priv, privep);
			}
		}
	}

	irqrestore(flags);
	return ret;
}

/*******************************************************************************
 * Name: stm32_ep_cancel
 *
 * Description:
 *   Cancel an I/O request previously sent to an endpoint
 *
 *******************************************************************************/

static int stm32_ep_cancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
	irqstate_t flags;

#ifdef CONFIG_DEBUG
	if (!ep || !req) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return -EINVAL;
	}
#endif

	usbtrace(TRACE_EPCANCEL, privep->epphy);

	flags = irqsave();

	/* FIXME: if the request is the first, then we need to flush the EP
	 *         otherwise just remove it from the list
	 *
	 *  but ... all other implementations cancel all requests ...
	 */

	stm32_req_cancel(privep, -ESHUTDOWN);
	irqrestore(flags);
	return OK;
}

/*******************************************************************************
 * Name: stm32_epout_setstall
 *
 * Description:
 *   Stall an OUT endpoint
 *
 *******************************************************************************/

static int stm32_epout_setstall(FAR struct stm32_ep_s *privep)
{
#if 1
	/* This implementation follows the requirements from the STM32 F4 reference
	 * manual.
	 */

	uint32_t regaddr;
	uint32_t regval;

	/* Put the core in the Global OUT NAK mode */

	stm32_enablegonak(privep);

	/* Disable and STALL the OUT endpoint by setting the EPDIS and STALL bits
	 * in the DOECPTL register.
	 */

	regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	regval |= (OTGHS_DOEPCTL_EPDIS | OTGHS_DOEPCTL_STALL);
	stm32_putreg(regval, regaddr);

	/* Wait for the EPDISD interrupt which indicates that the OUT
	 * endpoint is completely disabled.
	 */

#if 0							/* Doesn't happen */
	regaddr = STM32_OTGHS_DOEPINT(privep->epphy);
	while ((stm32_getreg(regaddr) & OTGHS_DOEPINT_EPDISD) == 0) ;
#else
	/* REVISIT: */
	up_udelay(10);
#endif

	/* Disable Global OUT NAK mode */

	stm32_disablegonak(privep);

	/* The endpoint is now stalled */

	privep->stalled = true;
	return OK;
#else
	/* This implementation follows the STMicro code example. */
	/* REVISIT: */

	uint32_t regaddr;
	uint32_t regval;

	/* Stall the OUT endpoint by setting the STALL bit in the DOECPTL register. */

	regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);
	regval |= OTGHS_DOEPCTL_STALL;
	stm32_putreg(regval, regaddr);

	/* The endpoint is now stalled */

	privep->stalled = true;
	return OK;
#endif
}

/*******************************************************************************
 * Name: stm32_epin_setstall
 *
 * Description:
 *   Stall an IN endpoint
 *
 *******************************************************************************/

static int stm32_epin_setstall(FAR struct stm32_ep_s *privep)
{
	uint32_t regaddr;
	uint32_t regval;

	/* Get the IN endpoint device control register */

	regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
	regval = stm32_getreg(regaddr);

	/* Then stall the endpoint */

	regval |= OTGHS_DIEPCTL_STALL;
	stm32_putreg(regval, regaddr);

	/* The endpoint is now stalled */

	privep->stalled = true;
	return OK;
}

/*******************************************************************************
 * Name: stm32_ep_setstall
 *
 * Description:
 *   Stall an endpoint
 *
 *******************************************************************************/

static int stm32_ep_setstall(FAR struct stm32_ep_s *privep)
{
	usbtrace(TRACE_EPSTALL, privep->epphy);

	/* Is this an IN endpoint? */

	if (privep->isin == 1) {
		return stm32_epin_setstall(privep);
	} else {
		return stm32_epout_setstall(privep);
	}
}

/*******************************************************************************
 * Name: stm32_ep_clrstall
 *
 * Description:
 *   Resume a stalled endpoint
 *
 *******************************************************************************/

static int stm32_ep_clrstall(FAR struct stm32_ep_s *privep)
{
	uint32_t regaddr;
	uint32_t regval;
	uint32_t stallbit;
	uint32_t data0bit;

	usbtrace(TRACE_EPRESUME, privep->epphy);

	/* Is this an IN endpoint? */

	if (privep->isin == 1) {
		/* Clear the stall bit in the IN endpoint device control register */

		regaddr = STM32_OTGHS_DIEPCTL(privep->epphy);
		stallbit = OTGHS_DIEPCTL_STALL;
		data0bit = OTGHS_DIEPCTL_SD0PID;
	} else {
		/* Clear the stall bit in the IN endpoint device control register */

		regaddr = STM32_OTGHS_DOEPCTL(privep->epphy);
		stallbit = OTGHS_DOEPCTL_STALL;
		data0bit = OTGHS_DOEPCTL_SD0PID;
	}

	/* Clear the stall bit */

	regval = stm32_getreg(regaddr);
	regval &= ~stallbit;

	/* Set the DATA0 pid for interrupt and bulk endpoints */

	if (privep->eptype == USB_EP_ATTR_XFER_INT || privep->eptype == USB_EP_ATTR_XFER_BULK) {
		/* Writing this bit sets the DATA0 PID */

		regval |= data0bit;
	}

	stm32_putreg(regval, regaddr);

	/* The endpoint is no longer stalled */

	privep->stalled = false;
	return OK;
}

/*******************************************************************************
 * Name: stm32_ep_stall
 *
 * Description:
 *   Stall or resume an endpoint
 *
 *******************************************************************************/

static int stm32_ep_stall(FAR struct usbdev_ep_s *ep, bool resume)
{
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
	irqstate_t flags;
	int ret;

	/* Set or clear the stall condition as requested */

	flags = irqsave();
	if (resume) {
		ret = stm32_ep_clrstall(privep);
	} else {
		ret = stm32_ep_setstall(privep);
	}
	irqrestore(flags);

	return ret;
}

/*******************************************************************************
 * Name: stm32_ep0_stall
 *
 * Description:
 *   Stall endpoint 0
 *
 *******************************************************************************/

static void stm32_ep0_stall(FAR struct stm32_usbdev_s *priv)
{
	stm32_epin_setstall(&priv->epin[EP0]);
	stm32_epout_setstall(&priv->epout[EP0]);
	priv->stalled = true;
	stm32_ep0out_ctrlsetup(priv);
}

/*******************************************************************************
 * Device operations
 *******************************************************************************/

/*******************************************************************************
 * Name: stm32_ep_alloc
 *
 * Description:
 *   Allocate an endpoint matching the parameters.
 *
 * Input Parameters:
 *   eplog  - 7-bit logical endpoint number (direction bit ignored).  Zero means
 *            that any endpoint matching the other requirements will suffice.  The
 *            assigned endpoint can be found in the eplog field.
 *   in     - true: IN (device-to-host) endpoint requested
 *   eptype - Endpoint type.  One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK,
 *            USB_EP_ATTR_XFER_INT}
 *
 *******************************************************************************/

static FAR struct usbdev_ep_s *stm32_ep_alloc(FAR struct usbdev_s *dev, uint8_t eplog, bool in, uint8_t eptype)
{
	FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
	uint8_t epavail;
	irqstate_t flags;
	int epphy;
	int epno = 0;

	usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog);

	/* Ignore any direction bits in the logical address */

	epphy = USB_EPNO(eplog);

	/* Get the set of available endpoints depending on the direction */

	flags = irqsave();
	epavail = priv->epavail[in];

	/* A physical address of 0 means that any endpoint will do */

	if (epphy > 0) {
		/* Otherwise, we will return the endpoint structure only for the requested
		 * 'logical' endpoint.  All of the other checks will still be performed.
		 *
		 * First, verify that the logical endpoint is in the range supported by
		 * by the hardware.
		 */

		if (epphy >= STM32_NENDPOINTS) {
			usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epphy);
			return NULL;
		}

		/* Remove all of the candidate endpoints from the bitset except for the
		 * this physical endpoint number.
		 */

		epavail &= (1 << epphy);
	}

	/* Is there an available endpoint? */

	if (epavail) {
		/* Yes.. Select the lowest numbered endpoint in the set of available
		 * endpoints.
		 */

		for (epno = 1; epno < STM32_NENDPOINTS; epno++) {
			uint8_t bit = 1 << epno;
			if ((epavail & bit) != 0) {
				/* Mark the endpoint no longer available */

				priv->epavail[in] &= ~(1 << epno);

				/* And return the pointer to the standard endpoint structure */

				irqrestore(flags);
				return in ? &priv->epin[epno].ep : &priv->epout[epno].ep;
			}
		}

		/* We should not get here */
	}

	usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOEP), (uint16_t)eplog);
	irqrestore(flags);
	return NULL;
}

/*******************************************************************************
 * Name: stm32_ep_free
 *
 * Description:
 *   Free the previously allocated endpoint
 *
 *******************************************************************************/

static void stm32_ep_free(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
	FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
	FAR struct stm32_ep_s *privep = (FAR struct stm32_ep_s *)ep;
	irqstate_t flags;

	usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy);

	if (priv && privep) {
		/* Mark the endpoint as available */

		flags = irqsave();
		priv->epavail[privep->isin] |= (1 << privep->epphy);
		irqrestore(flags);
	}
}

/*******************************************************************************
 * Name: stm32_getframe
 *
 * Description:
 *   Returns the current frame number
 *
 *******************************************************************************/

static int stm32_getframe(struct usbdev_s *dev)
{
	uint32_t regval;

	usbtrace(TRACE_DEVGETFRAME, 0);

	/* Return the last frame number of the last SOF detected by the hardware */

	regval = stm32_getreg(STM32_OTGHS_DSTS);
	return (int)((regval & OTGHS_DSTS_SOFFN_MASK) >> OTGHS_DSTS_SOFFN_SHIFT);
}

/*******************************************************************************
 * Name: stm32_wakeup
 *
 * Description:
 *   Exit suspend mode.
 *
 *******************************************************************************/

static int stm32_wakeup(struct usbdev_s *dev)
{
	FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;
	uint32_t regval;
	irqstate_t flags;

	usbtrace(TRACE_DEVWAKEUP, 0);

	/* Is wakeup enabled? */

	flags = irqsave();
	if (priv->wakeup) {
		/* Yes... is the core suspended? */

		regval = stm32_getreg(STM32_OTGHS_DSTS);
		if ((regval & OTGHS_DSTS_SUSPSTS) != 0) {
			/* Re-start the PHY clock and un-gate USB core clock (HCLK) */

#ifdef CONFIG_USBDEV_LOWPOWER
			regval = stm32_getreg(STM32_OTGHS_PCGCCTL);
			regval &= ~(OTGHS_PCGCCTL_STPPCLK | OTGHS_PCGCCTL_GATEHCLK);
			stm32_putreg(regval, STM32_OTGHS_PCGCCTL);
#endif
			/* Activate Remote wakeup signaling */

			regval = stm32_getreg(STM32_OTGHS_DCTL);
			regval |= OTGHS_DCTL_RWUSIG;
			stm32_putreg(regval, STM32_OTGHS_DCTL);
			up_mdelay(5);
			regval &= ~OTGHS_DCTL_RWUSIG;
			stm32_putreg(regval, STM32_OTGHS_DCTL);
		}
	}

	irqrestore(flags);
	return OK;
}

/*******************************************************************************
 * Name: stm32_selfpowered
 *
 * Description:
 *   Sets/clears the device self-powered feature
 *
 *******************************************************************************/

static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
	FAR struct stm32_usbdev_s *priv = (FAR struct stm32_usbdev_s *)dev;

	usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);

#ifdef CONFIG_DEBUG
	if (!dev) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return -ENODEV;
	}
#endif

	priv->selfpowered = selfpowered;
	return OK;
}

/*******************************************************************************
 * Name: stm32_pullup
 *
 * Description:
 *   Software-controlled connect to/disconnect from USB host
 *
 *******************************************************************************/

static int stm32_pullup(struct usbdev_s *dev, bool enable)
{
	uint32_t regval;

	usbtrace(TRACE_DEVPULLUP, (uint16_t)enable);

	irqstate_t flags = irqsave();
	regval = stm32_getreg(STM32_OTGHS_DCTL);
	if (enable) {
		/* Connect the device by clearing the soft disconnect bit in the DCTL
		 * register
		 */

		regval &= ~OTGHS_DCTL_SDIS;
	} else {
		/* Connect the device by setting the soft disconnect bit in the DCTL
		 * register
		 */

		regval |= OTGHS_DCTL_SDIS;
	}

	stm32_putreg(regval, STM32_OTGHS_DCTL);
	irqrestore(flags);
	return OK;
}

/*******************************************************************************
 * Name: stm32_setaddress
 *
 * Description:
 *   Set the devices USB address
 *
 *******************************************************************************/

static void stm32_setaddress(struct stm32_usbdev_s *priv, uint16_t address)
{
	uint32_t regval;

	/* Set the device address in the DCFG register */

	regval = stm32_getreg(STM32_OTGHS_DCFG);
	regval &= ~OTGHS_DCFG_DAD_MASK;
	regval |= ((uint32_t)address << OTGHS_DCFG_DAD_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DCFG);

	/* Are we now addressed?  (i.e., do we have a non-NULL device
	 * address?)
	 */

	if (address != 0) {
		priv->devstate = DEVSTATE_ADDRESSED;
		priv->addressed = true;
	} else {
		priv->devstate = DEVSTATE_DEFAULT;
		priv->addressed = false;
	}
}

/*******************************************************************************
 * Name: stm32_txfifo_flush
 *
 * Description:
 *   Flush the specific TX fifo.
 *
 *******************************************************************************/

static int stm32_txfifo_flush(uint32_t txfnum)
{
	uint32_t regval;
	uint32_t timeout;

	/* Initiate the TX FIFO flush operation */

	regval = OTGHS_GRSTCTL_TXFFLSH | txfnum;
	stm32_putreg(regval, STM32_OTGHS_GRSTCTL);

	/* Wait for the FLUSH to complete */

	for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) {
		regval = stm32_getreg(STM32_OTGHS_GRSTCTL);
		if ((regval & OTGHS_GRSTCTL_TXFFLSH) == 0) {
			break;
		}
	}

	/* Wait for 3 PHY Clocks */

	up_udelay(3);
	return OK;
}

/*******************************************************************************
 * Name: stm32_rxfifo_flush
 *
 * Description:
 *   Flush the RX fifo.
 *
 *******************************************************************************/

static int stm32_rxfifo_flush(void)
{
	uint32_t regval;
	uint32_t timeout;

	/* Initiate the RX FIFO flush operation */

	stm32_putreg(OTGHS_GRSTCTL_RXFFLSH, STM32_OTGHS_GRSTCTL);

	/* Wait for the FLUSH to complete */

	for (timeout = 0; timeout < STM32_FLUSH_DELAY; timeout++) {
		regval = stm32_getreg(STM32_OTGHS_GRSTCTL);
		if ((regval & OTGHS_GRSTCTL_RXFFLSH) == 0) {
			break;
		}
	}

	/* Wait for 3 PHY Clocks */

	up_udelay(3);
	return OK;
}

/*******************************************************************************
 * Name: stm32_swinitialize
 *
 * Description:
 *   Initialize all driver data structures.
 *
 *******************************************************************************/

static void stm32_swinitialize(FAR struct stm32_usbdev_s *priv)
{
	FAR struct stm32_ep_s *privep;
	int i;

	/* Initialize the device state structure */

	memset(priv, 0, sizeof(struct stm32_usbdev_s));

	priv->usbdev.ops = &g_devops;
	priv->usbdev.ep0 = &priv->epin[EP0].ep;

	priv->epavail[0] = STM32_EP_AVAILABLE;
	priv->epavail[1] = STM32_EP_AVAILABLE;

	priv->epin[EP0].ep.priv = priv;
	priv->epout[EP0].ep.priv = priv;

	/* Initialize the endpoint lists */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		/* Set endpoint operations, reference to driver structure (not
		 * really necessary because there is only one controller), and
		 * the physical endpoint number (which is just the index to the
		 * endpoint).
		 */

		privep = &priv->epin[i];
		privep->ep.ops = &g_epops;
		privep->dev = priv;
		privep->isin = 1;

		/* The index, i, is the physical endpoint address;  Map this
		 * to a logical endpoint address usable by the class driver.
		 */

		privep->epphy = i;
		privep->ep.eplog = STM32_EPPHYIN2LOG(i);

		/* Control until endpoint is activated */

		privep->eptype = USB_EP_ATTR_XFER_CONTROL;
		privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE;
	}

	/* Initialize the endpoint lists */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		/* Set endpoint operations, reference to driver structure (not
		 * really necessary because there is only one controller), and
		 * the physical endpoint number (which is just the index to the
		 * endpoint).
		 */

		privep = &priv->epout[i];
		privep->ep.ops = &g_epops;
		privep->dev = priv;

		/* The index, i, is the physical endpoint address;  Map this
		 * to a logical endpoint address usable by the class driver.
		 */

		privep->epphy = i;
		privep->ep.eplog = STM32_EPPHYOUT2LOG(i);

		/* Control until endpoint is activated */

		privep->eptype = USB_EP_ATTR_XFER_CONTROL;
		privep->ep.maxpacket = CONFIG_USBDEV_EP0_MAXSIZE;
	}
}

/*******************************************************************************
 * Name: stm32_hwinitialize
 *
 * Description:
 *   Configure the OTG HS core for operation.
 *
 *******************************************************************************/

static void stm32_hwinitialize(FAR struct stm32_usbdev_s *priv)
{
	uint32_t regval;
	uint32_t timeout;
	uint32_t address;
	int i;

	/* At start-up the core is in HS mode. */

	/* Disable global interrupts by clearing the GINTMASK bit in the GAHBCFG
	 * register; Set the TXFELVL bit in the GAHBCFG register so that TxFIFO
	 * interrupts will occur when the TxFIFO is truly empty (not just half full).
	 */

	stm32_putreg(OTGHS_GAHBCFG_TXFELVL, STM32_OTGHS_GAHBCFG);

	/* Set the PHYSEL bit in the GUSBCFG register to select the OTG HS serial
	 * transceiver: "This bit is always 1 with write-only access"
	 */

	regval = stm32_getreg(STM32_OTGHS_GUSBCFG);
	regval |= OTGHS_GUSBCFG_PHYSEL;
	stm32_putreg(regval, STM32_OTGHS_GUSBCFG);

	/* Common USB OTG core initialization */
	/* Reset after a PHY select and set Host mode.  First, wait for AHB master
	 * IDLE state.
	 */

	for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) {
		up_udelay(3);
		regval = stm32_getreg(STM32_OTGHS_GRSTCTL);
		if ((regval & OTGHS_GRSTCTL_AHBIDL) != 0) {
			break;
		}
	}

	/* Then perform the core soft reset. */

	stm32_putreg(OTGHS_GRSTCTL_CSRST, STM32_OTGHS_GRSTCTL);
	for (timeout = 0; timeout < STM32_READY_DELAY; timeout++) {
		regval = stm32_getreg(STM32_OTGHS_GRSTCTL);
		if ((regval & OTGHS_GRSTCTL_CSRST) == 0) {
			break;
		}
	}

	/* Wait for 3 PHY Clocks */

	up_udelay(3);

	/* Deactivate the power down */

	regval = (OTGHS_GCCFG_PWRDWN | OTGHS_GCCFG_VBUSASEN | OTGHS_GCCFG_VBUSBSEN);
#ifndef CONFIG_USBDEV_VBUSSENSING
	regval |= OTGHS_GCCFG_NOVBUSSENS;
#endif
#ifdef CONFIG_STM32_OTGHS_SOFOUTPUT
	regval |= OTGHS_GCCFG_SOFOUTEN;
#endif
	stm32_putreg(regval, STM32_OTGHS_GCCFG);
	up_mdelay(20);

	/* Force Device Mode */

	regval = stm32_getreg(STM32_OTGHS_GUSBCFG);
	regval &= ~OTGHS_GUSBCFG_FHMOD;
	regval |= OTGHS_GUSBCFG_FDMOD;
	stm32_putreg(regval, STM32_OTGHS_GUSBCFG);
	up_mdelay(50);

	/* Initialize device mode */
	/* Restart the PHY Clock */

	stm32_putreg(0, STM32_OTGHS_PCGCCTL);

	/* Device configuration register */

	regval = stm32_getreg(STM32_OTGHS_DCFG);
	regval &= ~OTGHS_DCFG_PFIVL_MASK;
	regval |= OTGHS_DCFG_PFIVL_80PCT;
	stm32_putreg(regval, STM32_OTGHS_DCFG);

	/* Set full speed PHY */

	regval = stm32_getreg(STM32_OTGHS_DCFG);
	regval &= ~OTGHS_DCFG_DSPD_MASK;
	regval |= OTGHS_DCFG_DSPD_FS;
	stm32_putreg(regval, STM32_OTGHS_DCFG);

	/* Set Rx FIFO size */

	stm32_putreg(STM32_RXFIFO_WORDS, STM32_OTGHS_GRXFSIZ);

	/* EP0 TX */

	address = STM32_RXFIFO_WORDS;
	regval = (address << OTGHS_DIEPTXF0_TX0FD_SHIFT) | (STM32_EP0_TXFIFO_WORDS << OTGHS_DIEPTXF0_TX0FSA_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DIEPTXF0);

	/* EP1 TX */

	address += STM32_EP0_TXFIFO_WORDS;
	regval = (address << OTGHS_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP1_TXFIFO_WORDS << OTGHS_DIEPTXF_INEPTXFD_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DIEPTXF1);

	/* EP2 TX */

	address += STM32_EP1_TXFIFO_WORDS;
	regval = (address << OTGHS_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP2_TXFIFO_WORDS << OTGHS_DIEPTXF_INEPTXFD_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DIEPTXF2);

	/* EP3 TX */

	address += STM32_EP2_TXFIFO_WORDS;
	regval = (address << OTGHS_DIEPTXF_INEPTXSA_SHIFT) | (STM32_EP3_TXFIFO_WORDS << OTGHS_DIEPTXF_INEPTXFD_SHIFT);
	stm32_putreg(regval, STM32_OTGHS_DIEPTXF3);

	/* Flush the FIFOs */

	stm32_txfifo_flush(OTGHS_GRSTCTL_TXFNUM_DALL);
	stm32_rxfifo_flush();

	/* Clear all pending Device Interrupts */

	stm32_putreg(0, STM32_OTGHS_DIEPMSK);
	stm32_putreg(0, STM32_OTGHS_DOEPMSK);
	stm32_putreg(0, STM32_OTGHS_DIEPEMPMSK);
	stm32_putreg(0xffffffff, STM32_OTGHS_DAINT);
	stm32_putreg(0, STM32_OTGHS_DAINTMSK);

	/* Configure all IN endpoints */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		regval = stm32_getreg(STM32_OTGHS_DIEPCTL(i));
		if ((regval & OTGHS_DIEPCTL_EPENA) != 0) {
			/* The endpoint is already enabled */

			regval = OTGHS_DIEPCTL_EPENA | OTGHS_DIEPCTL_SNAK;
		} else {
			regval = 0;
		}

		stm32_putreg(regval, STM32_OTGHS_DIEPCTL(i));
		stm32_putreg(0, STM32_OTGHS_DIEPTSIZ(i));
		stm32_putreg(0xff, STM32_OTGHS_DIEPINT(i));
	}

	/* Configure all OUT endpoints */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		regval = stm32_getreg(STM32_OTGHS_DOEPCTL(i));
		if ((regval & OTGHS_DOEPCTL_EPENA) != 0) {
			/* The endpoint is already enabled */

			regval = OTGHS_DOEPCTL_EPENA | OTGHS_DOEPCTL_SNAK;
		} else {
			regval = 0;
		}

		stm32_putreg(regval, STM32_OTGHS_DOEPCTL(i));
		stm32_putreg(0, STM32_OTGHS_DOEPTSIZ(i));
		stm32_putreg(0xff, STM32_OTGHS_DOEPINT(i));
	}

	/* Disable all interrupts. */

	stm32_putreg(0, STM32_OTGHS_GINTMSK);

	/* Clear any pending USB_OTG Interrupts */

	stm32_putreg(0xffffffff, STM32_OTGHS_GOTGINT);

	/* Clear any pending interrupts */

	stm32_putreg(0xbfffffff, STM32_OTGHS_GINTSTS);

	/* Disable the ULPI Clock enable in RCC AHB1 Register.  This must
	 * be done because if both the ULPI and the FS PHY clock enable bits
	 * are set at the same time, the ARM never awakens from WFI due to
	 * some bug / errata in the chip.
	 */

	regval = stm32_getreg(STM32_RCC_AHB1LPENR);
	regval &= ~RCC_AHB1ENR_OTGHSULPIEN;
	stm32_putreg(regval, STM32_RCC_AHB1LPENR);

	/* Enable the interrupts in the INTMSK */

	regval = (OTGHS_GINT_RXFLVL | OTGHS_GINT_USBSUSP | OTGHS_GINT_ENUMDNE | OTGHS_GINT_IEP | OTGHS_GINT_OEP | OTGHS_GINT_USBRST);

#ifdef CONFIG_USBDEV_ISOCHRONOUS
	regval |= (OTGHS_GINT_IISOIXFR | OTGHS_GINT_IISOOXFR);
#endif

#ifdef CONFIG_USBDEV_SOFINTERRUPT
	regval |= OTGHS_GINT_SOF;
#endif

#ifdef CONFIG_USBDEV_VBUSSENSING
	regval |= (OTGHS_GINT_OTG | OTGHS_GINT_SRQ);
#endif

#ifdef CONFIG_DEBUG_USB
	regval |= OTGHS_GINT_MMIS;
#endif

	stm32_putreg(regval, STM32_OTGHS_GINTMSK);

	/* Enable the USB global interrupt by setting GINTMSK in the global OTG
	 * HS AHB configuration register; Set the TXFELVL bit in the GAHBCFG
	 * register so that TxFIFO interrupts will occur when the TxFIFO is truly
	 * empty (not just half full).
	 */

	stm32_putreg(OTGHS_GAHBCFG_GINTMSK | OTGHS_GAHBCFG_TXFELVL, STM32_OTGHS_GAHBCFG);
}

/*******************************************************************************
 * Public Functions
 *******************************************************************************/

/*******************************************************************************
 * Name: up_usbinitialize
 *
 * Description:
 *   Initialize USB hardware.
 *
 * Assumptions:
 * - This function is called very early in the initialization sequence
 * - PLL and GIO pin initialization is not performed here but should been in
 *   the low-level  boot logic:  PLL1 must be configured for operation at 48MHz
 *   and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect
 *   LED.
 *
 *******************************************************************************/

void up_usbinitialize(void)
{
	/* At present, there is only a single OTG HS device support. Hence it is
	 * pre-allocated as g_otghsdev.  However, in most code, the private data
	 * structure will be referenced using the 'priv' pointer (rather than the
	 * global data) in order to simplify any future support for multiple devices.
	 */

	FAR struct stm32_usbdev_s *priv = &g_otghsdev;
	int ret;

	usbtrace(TRACE_DEVINIT, 0);

	/* Here we assume that:
	 *
	 * 1. GPIOA and OTG HS peripheral clocking has already been enabled as part
	 *    of the boot sequence.
	 * 2. Board-specific logic has already enabled other board specific GPIOs
	 *    for things like soft pull-up, VBUS sensing, power controls, and over-
	 *    current detection.
	 */

	/* Configure OTG HS alternate function pins
	 *
	 * PIN* SIGNAL      DIRECTION
	 * ---- ----------- ----------
	 * PA8  OTG_HS_SOF  SOF clock output
	 * PA9  OTG_HS_VBUS VBUS input for device, Driven by external regulator by
	 *                  host (not an alternate function)
	 * PA10 OTG_HS_ID   OTG ID pin (only needed in Dual mode)
	 * PA11 OTG_HS_DM   D- I/O
	 * PA12 OTG_HS_DP   D+ I/O
	 *
	 * *Pins may vary from device-to-device.
	 */

	stm32_configgpio(GPIO_OTGHS_DM);
	stm32_configgpio(GPIO_OTGHS_DP);
	stm32_configgpio(GPIO_OTGHS_ID);	/* Only needed for OTG */

	/* SOF output pin configuration is configurable. */

#ifdef CONFIG_STM32_OTGHS_SOFOUTPUT
	stm32_configgpio(GPIO_OTGHS_SOF);
#endif

	/* Uninitialize the hardware so that we know that we are starting from a
	 * known state. */

	up_usbuninitialize();

	/* Initialie the driver data structure */

	stm32_swinitialize(priv);

	/* Attach the OTG HS interrupt handler */

	ret = irq_attach(STM32_IRQ_OTGHS, stm32_usbinterrupt, NULL);
	if (ret < 0) {
		udbg("irq_attach failed\n", ret);
		goto errout;
	}

	/* Initialize the USB OTG core */

	stm32_hwinitialize(priv);

	/* Disconnect device */

	stm32_pullup(&priv->usbdev, false);

	/* Reset/Re-initialize the USB hardware */

	stm32_usbreset(priv);

	/* Enable USB controller interrupts at the NVIC */

	up_enable_irq(STM32_IRQ_OTGHS);

#ifdef CONFIG_ARCH_IRQPRIO
	/* Set the interrupt priority */

	up_prioritize_irq(STM32_IRQ_OTGHS, CONFIG_OTGHS_PRI);
#endif
	return;

errout:
	up_usbuninitialize();
}

/*******************************************************************************
 * Name: up_usbhsuninitialize
 *******************************************************************************/

void up_usbuninitialize(void)
{
	/* At present, there is only a single OTG HS device support. Hence it is
	 * pre-allocated as g_otghsdev.  However, in most code, the private data
	 * structure will be referenced using the 'priv' pointer (rather than the
	 * global data) in order to simplify any future support for multiple devices.
	 */

	FAR struct stm32_usbdev_s *priv = &g_otghsdev;
	irqstate_t flags;
	int i;

	usbtrace(TRACE_DEVUNINIT, 0);

	if (priv->driver) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0);
		usbdev_unregister(priv->driver);
	}

	/* Disconnect device */

	flags = irqsave();
	stm32_pullup(&priv->usbdev, false);
	priv->usbdev.speed = USB_SPEED_UNKNOWN;

	/* Disable and detach IRQs */

	up_disable_irq(STM32_IRQ_OTGHS);
	irq_detach(STM32_IRQ_OTGHS);

	/* Disable all endpoint interrupts */

	for (i = 0; i < STM32_NENDPOINTS; i++) {
		stm32_putreg(0xff, STM32_OTGHS_DIEPINT(i));
		stm32_putreg(0xff, STM32_OTGHS_DOEPINT(i));
	}

	stm32_putreg(0, STM32_OTGHS_DIEPMSK);
	stm32_putreg(0, STM32_OTGHS_DOEPMSK);
	stm32_putreg(0, STM32_OTGHS_DIEPEMPMSK);
	stm32_putreg(0, STM32_OTGHS_DAINTMSK);
	stm32_putreg(0xffffffff, STM32_OTGHS_DAINT);

	/* Flush the FIFOs */

	stm32_txfifo_flush(OTGHS_GRSTCTL_TXFNUM_DALL);
	stm32_rxfifo_flush();

	/* TODO: Turn off USB power and clocking */

	priv->devstate = DEVSTATE_DEFAULT;
	irqrestore(flags);
}

/*******************************************************************************
 * Name: usbdev_register
 *
 * Description:
 *   Register a USB device class driver. The class driver's bind() method will be
 *   called to bind it to a USB device driver.
 *
 *******************************************************************************/

int usbdev_register(struct usbdevclass_driver_s *driver)
{
	/* At present, there is only a single OTG HS device support. Hence it is
	 * pre-allocated as g_otghsdev.  However, in most code, the private data
	 * structure will be referenced using the 'priv' pointer (rather than the
	 * global data) in order to simplify any future support for multiple devices.
	 */

	FAR struct stm32_usbdev_s *priv = &g_otghsdev;
	int ret;

	usbtrace(TRACE_DEVREGISTER, 0);

#ifdef CONFIG_DEBUG
	if (!driver || !driver->ops->bind || !driver->ops->unbind || !driver->ops->disconnect || !driver->ops->setup) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return -EINVAL;
	}

	if (priv->driver) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0);
		return -EBUSY;
	}
#endif

	/* First hook up the driver */

	priv->driver = driver;

	/* Then bind the class driver */

	ret = CLASS_BIND(driver, &priv->usbdev);
	if (ret) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t)-ret);
		priv->driver = NULL;
	} else {
		/* Enable USB controller interrupts */

		up_enable_irq(STM32_IRQ_OTGHS);

		/* FIXME: nothing seems to call DEV_CONNECT(), but we need to set
		 *        the RS bit to enable the controller.  It kind of makes sense
		 *        to do this after the class has bound to us...
		 * GEN:   This bug is really in the class driver.  It should make the
		 *        soft connect when it is ready to be enumerated.  I have added
		 *        that logic to the class drivers but left this logic here.
		 */

		stm32_pullup(&priv->usbdev, true);
		priv->usbdev.speed = USB_SPEED_FULL;
	}

	return ret;
}

/*******************************************************************************
 * Name: usbdev_unregister
 *
 * Description:
 *   Un-register usbdev class driver.If the USB device is connected to a USB host,
 *   it will first disconnect().  The driver is also requested to unbind() and clean
 *   up any device state, before this procedure finally returns.
 *
 *******************************************************************************/

int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
	/* At present, there is only a single OTG HS device support. Hence it is
	 * pre-allocated as g_otghsdev.  However, in most code, the private data
	 * structure will be referenced using the 'priv' pointer (rather than the
	 * global data) in order to simplify any future support for multiple devices.
	 */

	FAR struct stm32_usbdev_s *priv = &g_otghsdev;
	irqstate_t flags;

	usbtrace(TRACE_DEVUNREGISTER, 0);

#ifdef CONFIG_DEBUG
	if (driver != priv->driver) {
		usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
		return -EINVAL;
	}
#endif

	/* Reset the hardware and cancel all requests.  All requests must be
	 * canceled while the class driver is still bound.
	 */

	flags = irqsave();
	stm32_usbreset(priv);
	irqrestore(flags);

	/* Unbind the class driver */

	CLASS_UNBIND(driver, &priv->usbdev);

	/* Disable USB controller interrupts */

	flags = irqsave();
	up_disable_irq(STM32_IRQ_OTGHS);

	/* Disconnect device */

	stm32_pullup(&priv->usbdev, false);

	/* Unhook the driver */

	priv->driver = NULL;
	irqrestore(flags);

	return OK;
}

#endif							/* CONFIG_USBDEV && CONFIG_STM32_OTGHSDEV */
